├── .circleci └── config.yml ├── .codacy.yml ├── .github └── FUNDING.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bower.json ├── dst ├── angular-modal-service.js ├── angular-modal-service.js.map ├── angular-modal-service.min.js └── angular-modal-service.min.js.map ├── index.js ├── karma.conf.js ├── package-lock.json ├── package.json ├── samples ├── complex │ ├── complex.html │ └── complexcontroller.js ├── custom │ ├── custom.css │ ├── custom.html │ └── customcontroller.js ├── index.html ├── sampleapp.js ├── vendor │ ├── bootstrap │ │ ├── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap-theme.css.map │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap-theme.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ └── js │ │ │ ├── bootstrap.js │ │ │ └── bootstrap.min.js │ └── jquery │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map └── yesno │ ├── yesno.html │ └── yesnocontroller.js ├── src └── angular-modal-service.js ├── test ├── basics.spec.js ├── bodyClass.spec.js ├── close.spec.js ├── closeModals.spec.js ├── component.spec.js ├── configOptions.spec.js ├── controller.spec.js ├── dom.spec.js ├── index.js ├── parameters.spec.js └── template.spec.js └── webpack.config.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: ~/project 5 | docker: 6 | - image: circleci/node:8-browsers 7 | environment: 8 | CHROME_BIN: "/usr/bin/google-chrome" 9 | steps: 10 | - checkout 11 | - run: 12 | name: update-npm 13 | command: 'sudo npm install -g npm@latest' 14 | - restore_cache: 15 | key: dependency-cache-{{ checksum "package.json" }} 16 | - run: 17 | name: install-npm 18 | command: npm install 19 | - save_cache: 20 | key: dependency-cache-{{ checksum "package.json" }} 21 | paths: 22 | - ./node_modules 23 | - run: 24 | name: test 25 | command: npm test 26 | - run: 27 | name: upload coverage 28 | command: bash <(curl -s https://codecov.io/bash) 29 | - store_artifacts: 30 | path: dst 31 | destination: dst 32 | - store_artifacts: 33 | path: test-results.xml 34 | destination: tests 35 | - store_artifacts: 36 | path: coverage 37 | destination: coverage 38 | - store_test_results: 39 | path: ./build/test-results.xml 40 | workflows: 41 | version: 2 42 | build: 43 | jobs: 44 | - build: 45 | # All branches, all tags. 46 | filters: 47 | tags: 48 | only: /.*/ 49 | -------------------------------------------------------------------------------- /.codacy.yml: -------------------------------------------------------------------------------- 1 | exclude_paths: 2 | - ./dst/. 3 | - '*.min.js' 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Support 'GitHub Sponsors' funding. 2 | github: dwmkerr 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | build 4 | .DS_Store 5 | .idea 6 | 7 | artifacts/ 8 | 9 | 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [0.15.6](https://github.com/dwmkerr/angular-modal-service/compare/v0.15.5...v0.15.6) (2020-04-28) 6 | 7 | ### [0.15.5](https://github.com/dwmkerr/angular-modal-service/compare/v0.15.4...v0.15.5) (2020-04-28) 8 | 9 | ### [0.15.4](https://github.com/dwmkerr/angular-modal-service/compare/v0.15.3...v0.15.4) (2020-04-28) 10 | 11 | ## v0.15.0 12 | 13 | * Support for a globally configurable close timeout. Thanks [decherneyge](https://github.com/decherneyge). 14 | * Support for selector for `appendElement`. Thanks [decherneyge](https://github.com/decherneyge). 15 | * Tooling version updates. 16 | 17 | ## v0.7.12 18 | 19 | * Support for configuration of the `$locationChangeSuccess` event. Thanks [smalbs](https://github.com/smalbs). 20 | 21 | ## v0.6.12 22 | 23 | * When a model is opened, we blur any active element. Thanks [samx](https://github.com/samwx)! 24 | 25 | ## v0.6.11 26 | 27 | * Robustness for 'locationChangeEvent'. 28 | * Robustness if the body element changs. 29 | * Addded `bower` as a dev dependency. Run `bower install` as an `npm` `postinstall` step, which makes initial setup on a clean machine slightly easier. 30 | 31 | ## v0.6.10 32 | 33 | * Added support for using a custom parent scope for the controller scope, via the `scope` option. 34 | * Fixed a bug with controllerAs vs `controller as`. 35 | 36 | ## v0.6.6 37 | 38 | * Removed calls to $q `catch` and replaced with `then(null, f)` so that the code works in 39 | IE8 (ECMAScript 3). 40 | 41 | ## v0.6.5 42 | 43 | * Reverted changes below as they led to a bug with injected `$element` in modal controller. 44 | Added a test to protect against this case in the future. 45 | 46 | ## v0.6.4 47 | 48 | * Merged `scope` option field. Updated readme. 49 | 50 | ## v0.6.3 51 | 52 | * Fixed memory leak. 53 | 54 | ## v0.6.2 55 | 56 | * Tidied up logic for cleanup. 57 | * Fixed issue with vendor files. 58 | 59 | ## v0.6.1 60 | 61 | * Moved from grunt to gulp. 62 | 63 | ## v0.6 64 | 65 | ## v0.5 66 | 67 | * Updated the dependencies to use AngularJS 1.3 and upwards. 68 | 69 | ## v0.4 70 | 71 | ### Features 72 | 73 | * The modal can now be added to any specific element in the DOM. If 74 | unspecified, the modal is added to the `body`, as before. 75 | Thanks [cointilt](https://github.com/cointilt)! 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Dave Kerr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-modal-service 2 | 3 | [![CircleCI](https://circleci.com/gh/dwmkerr/angular-modal-service.svg?style=shield)](https://circleci.com/gh/dwmkerr/angular-modal-service) 4 | [![codecov](https://codecov.io/gh/dwmkerr/angular-modal-service/branch/master/graph/badge.svg)](https://codecov.io/gh/dwmkerr/angular-modal-service) 5 | [![Dependencies](https://david-dm.org/dwmkerr/angular-modal-service.svg?theme=shields.io)](https://david-dm.org/dwmkerr/angular-modal-service) 6 | [![Dev Dependencies](https://david-dm.org/dwmkerr/angular-modal-service/dev-status.svg?theme=shields.io)](https://david-dm.org/dwmkerr/angular-modal-service#info=devDependencies) 7 | [![Greenkeeper badge](https://badges.greenkeeper.io/dwmkerr/angular-modal-service.svg)](https://greenkeeper.io/) [![GuardRails badge](https://badges.production.guardrails.io/dwmkerr/angular-modal-service.svg)](https://www.guardrails.io) 8 | 9 | Modal service for AngularJS - supports creating popups and modals via a service. Full support for Angular 1.5+ components. See [a quick fiddle](http://jsfiddle.net/dwmkerr/8MVLJ/) or a full set of samples at [dwmkerr.github.io/angular-modal-service](http://dwmkerr.github.io/angular-modal-service). 10 | 11 | 12 | 13 | * [Usage](#usage) 14 | * [Support for AngularJS 1.5.x Components](#support-for-angularjs-15x-components) 15 | * [ShowModal Options](#showmodal-options) 16 | * [The Modal Object](#the-modal-object) 17 | * [The Modal Controller](#the-modal-controller) 18 | * [Closing All Modals](#closing-all-modals) 19 | * [Animation](#animation) 20 | * [Error Handing](#error-handing) 21 | * [Global Options Configuration](#global-options-configuration) 22 | * [Developing](#developing) 23 | * [Tests](#tests) 24 | * [Releasing](#releasing) 25 | * [FAQ](#faq) 26 | * [Thanks](#thanks) 27 | 28 | 29 | 30 | ## Usage 31 | 32 | Install with Bower (or NPM): 33 | 34 | ```bash 35 | bower install angular-modal-service 36 | # or... 37 | npm install angular-modal-service 38 | ``` 39 | 40 | Then reference the minified script: 41 | 42 | ```html 43 | 44 | ``` 45 | 46 | Specify the modal service as a dependency of your application: 47 | 48 | ```js 49 | var app = angular.module('sampleapp', ['angularModalService']); 50 | ``` 51 | 52 | Now just inject the modal service into any controller, service or directive where you need it. 53 | 54 | ```js 55 | app.controller('SampleController', ["$scope", "ModalService", function($scope, ModalService) { 56 | 57 | $scope.showAModal = function() { 58 | 59 | // Just provide a template url, a controller and call 'showModal'. 60 | ModalService.showModal({ 61 | templateUrl: "yesno/yesno.html", 62 | controller: "YesNoController" 63 | }).then(function(modal) { 64 | // The modal object has the element built, if this is a bootstrap modal 65 | // you can call 'modal' to show it, if it's a custom modal just show or hide 66 | // it as you need to. 67 | modal.element.modal(); 68 | modal.close.then(function(result) { 69 | $scope.message = result ? "You said Yes" : "You said No"; 70 | }); 71 | }); 72 | 73 | }; 74 | 75 | }]); 76 | ``` 77 | 78 | Calling `showModal` returns a promise which is resolved when the modal DOM element is created 79 | and the controller for it is created. The promise returns a `modal` object which contains the 80 | element created, the controller, the scope and two promises: `close` and `closed`. Both are 81 | resolved to the result of the modal close function, but `close` is resolved as soon as the 82 | modal close function is called, while `closed` is only resolved once the modal has finished 83 | animating and has been completely removed from the DOM. 84 | 85 | The modal controller can be any controller that you like, just remember that it is always 86 | provided with one extra parameter - the `close` function. Here's an example controller 87 | for a bootstrap modal: 88 | 89 | ```js 90 | app.controller('SampleModalController', function($scope, close) { 91 | 92 | $scope.dismissModal = function(result) { 93 | close(result, 200); // close, but give 200ms for bootstrap to animate 94 | }; 95 | 96 | }); 97 | ``` 98 | 99 | The `close` function is automatically injected to the modal controller and takes the result 100 | object (which is passed to the `close` and `closed` promises used by the caller). It can 101 | take an optional second parameter, the number of milliseconds to wait before destroying the 102 | DOM element. This is so that you can have a delay before destroying the DOM element if you 103 | are animating the closure. See [Global Config](#global-options-configuration) for setting a default delay. 104 | 105 | Now just make sure the `close` function is called by your modal controller when the modal 106 | should be closed and that's it. Quick hint - if you are using Bootstrap for your modals, 107 | then make sure the modal template only contains one root level element, see the [FAQ](#faq) 108 | for the gritty details of why. 109 | 110 | To pass data into the modal controller, use the `inputs` field of the modal options. For example: 111 | 112 | ```js 113 | ModalService.showModal({ 114 | templateUrl: "exampletemplate.html", 115 | controller: "ExampleController", 116 | inputs: { 117 | name: "Fry", 118 | year: 3001 119 | } 120 | }) 121 | ``` 122 | 123 | injects the `name` and `year` values into the controller: 124 | 125 | ```js 126 | app.controller('ExampleController', function($scope, name, year, close) { 127 | }); 128 | ``` 129 | 130 | You can also provide a controller function directly to the modal, with or without the `controllerAs` attribute. 131 | But if you provide `controller` attribute with `as` syntax and `controllerAs` attribute together, `controllerAs` 132 | will have high priority. 133 | 134 | ```js 135 | ModalService.showModal({ 136 | template: "
Fry lives in {{futurama.city}}
", 137 | controller: function() { 138 | this.city = "New New York"; 139 | }, 140 | controllerAs : "futurama" 141 | }) 142 | 143 | ``` 144 | 145 | 146 | ## Support for AngularJS 1.5.x Components 147 | 148 | It's also possible to specify a component, rather than a template and controller. This can be done by providing a `component` and an optional `bindings` value to the `showModal` function. 149 | 150 | ```js 151 | ModalService.showModal({ 152 | component: 'myComponent', 153 | bindings: { 154 | name: 'Foo', 155 | myRecord: { id: '123' } 156 | } 157 | }) 158 | ``` 159 | 160 | 161 | ### ShowModal Options 162 | 163 | The `showModal` function takes an object with these fields: 164 | 165 | * `controller`: The name of the controller to create. It could be a function. 166 | * `controllerAs` : The name of the variable on the scope instance of the controller is assigned to - (optional). 167 | * `templateUrl`: The URL of the HTML template to use for the modal. 168 | * `template`: If `templateUrl` is not specified, you can specify `template` as raw 169 | HTML for the modal. 170 | * `inputs`: A set of values to pass as inputs to the controller. Each value provided 171 | is injected into the controller constructor. 172 | * `component`: Renders a modal with the provided component as its template 173 | * `bindings`: Optional. If `component` is provided, all properties in `bindings` will be bound to the rendered `component`. 174 | * `appendElement`: The custom angular element or selector (such as `#element-id`) to append the modal to instead of default `body` element. 175 | * `scope`: Optional. If provided, the modal controller will use a new scope as a child of `scope` (created by calling `scope.$new()`) rather than a new scope created as a child of `$rootScope`. 176 | * `bodyClass`: Optional. The custom css class to append to the body while the modal is open (optional, useful when not using Bootstrap). 177 | * `preClose`: Optional. A function which will be called before the process of closing a modal starts. The signature is `function preClose(modal, result, delay)`. It is provided the `modal` object, the `result` which was passed to `close` and the `delay` which was passed to close. 178 | * `locationChangeSuccess`: Optional. Allows the closing of the modal when the location changes to be configured. If no value is set, the modal is closed immediately when the `$locationChangeSuccess` event fires. If `false` is set, event is not fired. If a number `n` is set, then the event fires after `n` milliseconds. 179 | 180 | ### The Modal Object 181 | 182 | The `modal` object returned by `showModal` has this structure: 183 | 184 | * `modal.element` - The created DOM element. This is a jquery lite object (or jquery if full 185 | jquery is used). If you are using a bootstrap modal, you can call `modal` on this object 186 | to show the modal. 187 | * `modal.scope` - The new scope created for the modal DOM and controller. 188 | * `modal.controller` - The new controller created for the modal. 189 | * `modal.close` - A promise which is resolved when the modal `close` function is called. 190 | * `modal.closed` - A promise which is resolved once the modal has finished animating out of the DOM. 191 | 192 | ### The Modal Controller 193 | 194 | The controller that is used for the modal always has one extra parameter injected, a function 195 | called `close`. Call this function with any parameter (the result). This result parameter is 196 | then passed as the parameter of the `close` and `closed` promises used by the caller. 197 | 198 | ### Closing All Modals 199 | 200 | Sometimes you may way to forcibly close all open modals, for example if you are going to transition routes. You can use the `ModalService.closeModals` function for this: 201 | 202 | ```js 203 | ModalService.closeModals(optionalResult, optionalDelay); 204 | ``` 205 | 206 | The `optionalResult` parameter is pased into all `close` promises, the `optionalDelay` parameter has the same effect as the controller `close` function delay parameter. 207 | 208 | ### Animation 209 | 210 | `ModalService` cooperates with Angular's `$animate` service to allow easy implementation of 211 | custom animation. Specifically, `showModal` will trigger the `ng-enter` hook, and calling 212 | `close` will trigger the `ng-leave` hook. For example, if the `ngAnimate` module is 213 | installed, the following CSS rules will add fade in/fade out animations to a modal with the 214 | class `modal`: 215 | 216 | ```css 217 | .modal.ng-enter { 218 | transition: opacity .5s ease-out; 219 | opacity: 0; 220 | } 221 | .modal.ng-enter.ng-enter-active { 222 | opacity: 1; 223 | } 224 | .modal.ng-leave { 225 | transition: opacity .5s ease-out; 226 | opacity: 1; 227 | } 228 | .modal.ng-leave.ng-leave-active { 229 | opacity: 0; 230 | } 231 | ``` 232 | 233 | ### Error Handing 234 | 235 | As the `ModalService` exposes only one function, `showModal`, error handling is always performed in the same way. 236 | The `showModal` function returns a promise - if any part of the process fails, the promise will be rejected, meaning 237 | that a promise error handling function or `catch` function can be used to get the error details: 238 | 239 | ```js 240 | ModalService.showModal({ 241 | templateUrl: "some/template.html", 242 | controller: "SomeController" 243 | }).then(function(modal) { 244 | // only called on success... 245 | }).catch(function(error) { 246 | // error contains a detailed error message. 247 | console.log(error); 248 | }); 249 | ``` 250 | 251 | ### Global Options Configuration 252 | 253 | To configure the default options that will apply to all modals call `configureOptions` on the `ModalServiceProvider`. 254 | 255 | ```js 256 | app.config(["ModalServiceProvider", function(ModalServiceProvider) { 257 | 258 | ModalServiceProvider.configureOptions({closeDelay:500}); 259 | 260 | }]); 261 | ``` 262 | 263 | Here are the available global options: 264 | * `closeDelay` - This sets the default number of milliseconds to use in the close handler. This delay will also be used in the `closeModals` method and as the default for `locationChangeSuccess`. 265 | 266 | ## Developing 267 | 268 | To work with the code, just run: 269 | 270 | ``` 271 | npm install 272 | npm test 273 | npm start 274 | ``` 275 | 276 | The dependencies will install, the tests will be run (always a useful sanity check after a clean checkout) and the code will run. You can open the browser at localhost:8080 to see the samples. As you change the code in the `src/` folder, it will be re-built and the browser will be updated. 277 | 278 | The easiest way to adapt the code is to play with some of the examples in the ``samples`` folder. 279 | 280 | ## Tests 281 | 282 | Run tests with: 283 | 284 | ``` 285 | npm test 286 | ``` 287 | 288 | A coverage report is written to `build\coverage`. 289 | 290 | Debug tests with: 291 | 292 | ``` 293 | npm run test-debug 294 | ``` 295 | 296 | This will run the tests in Chrome, allowing you to debug. 297 | 298 | ## Releasing 299 | 300 | To create a release: 301 | 302 | - Create the `dst` pack with `npm run build` 303 | - Merge your work to master 304 | - Use `npm run release` to tag, bump the version numbers and update the changelog 305 | - Push and deploy `git push --follow-tags && npm publish` 306 | 307 | ## FAQ 308 | 309 | Having problems? Check this FAQ first. 310 | 311 | **I'm using a Bootstrap Modal and the backdrop doesn't fade away** 312 | 313 | This can happen if your modal template contains more than one top level element. 314 | Imagine this case: 315 | 316 | ```html 317 | 318 |
...some modal
319 | ``` 320 | 321 | When you create the modal, the Angular Modal Service will add both of these elements 322 | to the page, then pass the elements to you as a jQuery selector. When you call bootstrap's 323 | `modal` function on it, like this: 324 | 325 | ```js 326 | modal.element.modal(); 327 | ``` 328 | 329 | It will try and make both elements into a modal. This means both elements will get a backdrop. 330 | In this case, either remove the extra elements, or find the specific element you need 331 | from the provided `modal.element` property. 332 | 333 | **The backdrop STILL does not fade away after I call `close` OR I don't want to use the 'data-dismiss' attribute on a button, how can I close a modal manually?** 334 | 335 | You can check the 'Complex' sample ([complexcontroller.js](samples/complex/complexcontroller.js)). The 'Cancel' button closes without using the `data-dismiss` attribute. In this case, just use the `preClose` option to ensure the bootstrap modal is removed: 336 | 337 | ```js 338 | ModalService.showModal({ 339 | templateUrl: "some/bootstrap-template.html", 340 | controller: "SomeController", 341 | preClose: (modal) => { modal.element.modal('hide'); } 342 | }).then(function(modal) { 343 | // etc 344 | }); 345 | ``` 346 | 347 | Another option is to grab the modal element in your controller, then call the bootstrap `modal` function 348 | to manually close the modal. Then call the `close` function as normal: 349 | 350 | ```js 351 | app.controller('ExampleModalController', [ 352 | '$scope', '$element', 'close', 353 | function($scope, $element, close) { 354 | 355 | $scope.closeModal = function() { 356 | 357 | // Manually hide the modal using bootstrap. 358 | $element.modal('hide'); 359 | 360 | // Now close as normal, but give 500ms for bootstrap to animate 361 | close(null, 500); 362 | }; 363 | 364 | }]); 365 | ``` 366 | 367 | **I'm using a Bootstrap Modal and the dialog doesn't show up** 368 | 369 | Code is entered exactly as shown the example but when the showAModal() function fires the modal template html is appended to the body while the console outputs: 370 | 371 | ``` 372 | TypeError: undefined is not a function 373 | ``` 374 | 375 | Pointing to the code: `modal.element.modal();`. This occurs if you are using a Bootstap modal but have not included the Bootstrap JavaScript. The recommendation is to include the modal JavaScript before AngularJS. 376 | 377 | **How can I prevent a Bootstrap modal from being closed?** 378 | 379 | If you are using a bootstrap modal and want to make sure that only the `close` function will close the modal (not a click outside or escape), use the following attributes: 380 | 381 | ```html 382 | 35 | -------------------------------------------------------------------------------- /samples/complex/complexcontroller.js: -------------------------------------------------------------------------------- 1 | var app = angular.module('sampleapp'); 2 | 3 | app.controller('ComplexController', [ 4 | '$scope', '$element', 'title', 'close', 5 | function ($scope, $element, title, close) { 6 | 7 | $scope.name = null; 8 | $scope.age = null; 9 | $scope.title = title; 10 | 11 | $scope.wasClosed = false; 12 | 13 | // This close function doesn't need to use jQuery or bootstrap, because 14 | // the button has the 'data-dismiss' attribute. 15 | $scope.close = function () { 16 | 17 | //mark the modal as handled 18 | $scope.wasClosed = true; 19 | 20 | close({ 21 | name: $scope.name, 22 | age: $scope.age, 23 | source: 'Ok\'ed' 24 | }, 500); // close, but give 500ms for bootstrap to animate 25 | }; 26 | 27 | // This cancel function must use the bootstrap, 'modal' function because 28 | // the doesn't have the 'data-dismiss' attribute. 29 | $scope.cancel = function () { 30 | 31 | //mark the modal as handled 32 | $scope.wasClosed = true; 33 | 34 | // Manually hide the modal. 35 | $element.modal('hide'); 36 | 37 | // Now call close, returning control to the caller. 38 | close({ 39 | name: $scope.name, 40 | age: $scope.age, 41 | source: 'Cancelled' 42 | }, 500); // close, but give 500ms for bootstrap to animate 43 | }; 44 | 45 | // The abort function doesn't need to use jQuery or bootstrap, because 46 | // the backdrop click already dismisses the modal and the X has the 'data-dismiss' attribute. 47 | $scope.abort = function () { 48 | //mark the modal as handled 49 | $scope.wasClosed = true; 50 | 51 | close({ 52 | name: $scope.name, 53 | age: $scope.age, 54 | source: 'Aborted' 55 | }, 500); // close, but give 500ms for bootstrap to animate 56 | }; 57 | 58 | var backgroundClickHandler = function (e) { 59 | 60 | //if the modal was already closed short circuit 61 | if ($scope.wasClosed) return; 62 | 63 | //call the close functionality 64 | $scope.abort(); 65 | 66 | //remove the listener 67 | $element.off('hidden.bs.modal', backgroundClickHandler); 68 | }; 69 | 70 | //listen for when the modal is dismissed and close it 71 | $element.on('hidden.bs.modal', backgroundClickHandler); 72 | 73 | }]); -------------------------------------------------------------------------------- /samples/custom/custom.css: -------------------------------------------------------------------------------- 1 | 2 | #overlay { 3 | position: fixed; 4 | left: 25%; 5 | top: 25%; 6 | padding: 25px; 7 | border: 2px solid black; 8 | background-color: #ffffff; 9 | width: 50%; 10 | height: 50%; 11 | z-index: 100; 12 | } 13 | #fade { 14 | position: fixed; 15 | left: 0%; 16 | top: 0%; 17 | background-color: black; 18 | -moz-opacity: 0.7; 19 | opacity: .70; 20 | filter: alpha(opacity=70); 21 | width: 100%; 22 | height: 100%; 23 | z-index: 90; 24 | } 25 | 26 | body.custom-modal-open #fade { 27 | background-color: darkred; 28 | -moz-opacity: 0.9; 29 | opacity: .90; 30 | filter: alpha(opacity=90); 31 | } 32 | 33 | #custom-modal.ng-enter { 34 | transition: opacity .5s ease-out; 35 | opacity: 0; 36 | } 37 | #custom-modal.ng-enter.ng-enter-active { 38 | opacity: 1; 39 | } 40 | #custom-modal.ng-leave { 41 | transition: opacity .5s ease-out; 42 | opacity: 1; 43 | } 44 | #custom-modal.ng-leave.ng-leave-active { 45 | opacity: 0; 46 | } 47 | -------------------------------------------------------------------------------- /samples/custom/custom.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | This is a custom modal. The angular-modal-service doesn't depend on bootstrap, you 4 | can use any modal you want. The background colour is altered by the custom class 5 | applied to the body. 6 | Close 7 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /samples/custom/customcontroller.js: -------------------------------------------------------------------------------- 1 | var app = angular.module('sampleapp'); 2 | 3 | app.controller('CustomController', ['$scope', 'close', function($scope, close) { 4 | 5 | $scope.close = close; 6 | 7 | }]); 8 | -------------------------------------------------------------------------------- /samples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Angular Modal Service - Samples 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 43 | 44 | 48 | 49 | 50 | 56 | 57 | 78 | 79 |
80 | 81 |
82 |

Angular Modal Service Samples

83 |

Samples for the only Angular Modal Service you'll ever need.

84 |
85 | 86 |
87 | 88 |

Tip: Close all modals at any time by hitting the * key!

89 | 90 |
91 |

A Simple Yes/No Modal

92 | 93 |

This sample shows a Bootstrap Modal with the options 'Yes' and 'No'.

94 |

It demonstrates how to show a Bootstrap Modal, how to show a modal and how 95 | to retrieve data from the modal.

96 | 97 |

Show Yes No

98 | 99 |
{{yesNoResult}}
100 |

See the code that makes up this sample:

101 | 106 |
107 | 108 |
109 | 110 | 111 |
112 |

A Complex Modal

113 | 114 |

This more complicated modal contains a form, showing how the modal template can 115 | be customised. It also has a controller with data injected by the caller and 116 | returns more complex data.

117 | 118 |

Show Complex

119 | 120 |
{{complexResult}}
121 |

See the code that makes up this sample:

122 | 127 |
128 | 129 |
130 | 131 | 132 |
133 |

A Custom Modal

134 | 135 |

This sample shows how you can use any kind of modal, not just Bootstrap modals.

136 | 137 |

Show Custom

138 | 139 |
{{customResult}}
140 |

See the code that makes up this sample:

141 | 146 |
147 | 148 | 149 |
150 | 151 | 152 | 162 | 163 | -------------------------------------------------------------------------------- /samples/sampleapp.js: -------------------------------------------------------------------------------- 1 | // Build our app module, with a dependency on the angular modal service. 2 | var app = angular.module('sampleapp', ['angularModalService', 'ngAnimate']); 3 | 4 | app.config(["ModalServiceProvider", function(ModalServiceProvider) { 5 | //uncomment this line to set a default close delay 6 | //ModalServiceProvider.configureOptions({closeDelay:500}); 7 | 8 | }]); 9 | 10 | app.controller('SampleController', ['$scope', 'ModalService', function($scope, ModalService) { 11 | 12 | $scope.yesNoResult = null; 13 | $scope.complexResult = null; 14 | $scope.customResult = null; 15 | 16 | $scope.showYesNo = function() { 17 | 18 | ModalService.showModal({ 19 | templateUrl: "yesno/yesno.html", 20 | controller: "YesNoController", 21 | preClose: (modal) => { modal.element.modal('hide'); } 22 | }).then(function(modal) { 23 | modal.element.modal(); 24 | modal.close.then(function(result) { 25 | $scope.yesNoResult = result ? "You said Yes" : "You didn't say Yes"; 26 | }); 27 | }); 28 | 29 | }; 30 | 31 | $scope.showComplex = function() { 32 | 33 | ModalService.showModal({ 34 | templateUrl: "complex/complex.html", 35 | controller: "ComplexController", 36 | preClose: (modal) => { modal.element.modal('hide'); }, 37 | inputs: { 38 | title: "A More Complex Example" 39 | } 40 | }).then(function(modal) { 41 | modal.element.modal(); 42 | 43 | modal.close.then(function(result) { 44 | if (!result) { 45 | $scope.complexResult = "Modal forcibly closed..." 46 | } else { 47 | $scope.complexResult = "Name: " + result.name + ", age: " + result.age + ", source: " + result.source; 48 | } 49 | }); 50 | }); 51 | 52 | }; 53 | 54 | $scope.showCustom = function() { 55 | 56 | ModalService.showModal({ 57 | templateUrl: "custom/custom.html", 58 | controller: "CustomController", 59 | bodyClass: "custom-modal-open" 60 | }).then(function(modal) { 61 | modal.close.then(function(result) { 62 | $scope.customResult = "All good!"; 63 | }); 64 | }); 65 | 66 | }; 67 | 68 | $scope.keyPress = function(value) { 69 | if (value.keyCode == 42) { 70 | ModalService.closeModals(null, 500); 71 | } 72 | }; 73 | 74 | }]); 75 | -------------------------------------------------------------------------------- /samples/vendor/bootstrap/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.6 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | .btn-default, 7 | .btn-primary, 8 | .btn-success, 9 | .btn-info, 10 | .btn-warning, 11 | .btn-danger { 12 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 13 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 14 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | } 16 | .btn-default:active, 17 | .btn-primary:active, 18 | .btn-success:active, 19 | .btn-info:active, 20 | .btn-warning:active, 21 | .btn-danger:active, 22 | .btn-default.active, 23 | .btn-primary.active, 24 | .btn-success.active, 25 | .btn-info.active, 26 | .btn-warning.active, 27 | .btn-danger.active { 28 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 29 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | } 31 | .btn-default.disabled, 32 | .btn-primary.disabled, 33 | .btn-success.disabled, 34 | .btn-info.disabled, 35 | .btn-warning.disabled, 36 | .btn-danger.disabled, 37 | .btn-default[disabled], 38 | .btn-primary[disabled], 39 | .btn-success[disabled], 40 | .btn-info[disabled], 41 | .btn-warning[disabled], 42 | .btn-danger[disabled], 43 | fieldset[disabled] .btn-default, 44 | fieldset[disabled] .btn-primary, 45 | fieldset[disabled] .btn-success, 46 | fieldset[disabled] .btn-info, 47 | fieldset[disabled] .btn-warning, 48 | fieldset[disabled] .btn-danger { 49 | -webkit-box-shadow: none; 50 | box-shadow: none; 51 | } 52 | .btn-default .badge, 53 | .btn-primary .badge, 54 | .btn-success .badge, 55 | .btn-info .badge, 56 | .btn-warning .badge, 57 | .btn-danger .badge { 58 | text-shadow: none; 59 | } 60 | .btn:active, 61 | .btn.active { 62 | background-image: none; 63 | } 64 | .btn-default { 65 | text-shadow: 0 1px 0 #fff; 66 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 67 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 68 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 69 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 70 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 71 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 72 | background-repeat: repeat-x; 73 | border-color: #dbdbdb; 74 | border-color: #ccc; 75 | } 76 | .btn-default:hover, 77 | .btn-default:focus { 78 | background-color: #e0e0e0; 79 | background-position: 0 -15px; 80 | } 81 | .btn-default:active, 82 | .btn-default.active { 83 | background-color: #e0e0e0; 84 | border-color: #dbdbdb; 85 | } 86 | .btn-default.disabled, 87 | .btn-default[disabled], 88 | fieldset[disabled] .btn-default, 89 | .btn-default.disabled:hover, 90 | .btn-default[disabled]:hover, 91 | fieldset[disabled] .btn-default:hover, 92 | .btn-default.disabled:focus, 93 | .btn-default[disabled]:focus, 94 | fieldset[disabled] .btn-default:focus, 95 | .btn-default.disabled.focus, 96 | .btn-default[disabled].focus, 97 | fieldset[disabled] .btn-default.focus, 98 | .btn-default.disabled:active, 99 | .btn-default[disabled]:active, 100 | fieldset[disabled] .btn-default:active, 101 | .btn-default.disabled.active, 102 | .btn-default[disabled].active, 103 | fieldset[disabled] .btn-default.active { 104 | background-color: #e0e0e0; 105 | background-image: none; 106 | } 107 | .btn-primary { 108 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); 109 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); 110 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); 111 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); 112 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); 113 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 114 | background-repeat: repeat-x; 115 | border-color: #245580; 116 | } 117 | .btn-primary:hover, 118 | .btn-primary:focus { 119 | background-color: #265a88; 120 | background-position: 0 -15px; 121 | } 122 | .btn-primary:active, 123 | .btn-primary.active { 124 | background-color: #265a88; 125 | border-color: #245580; 126 | } 127 | .btn-primary.disabled, 128 | .btn-primary[disabled], 129 | fieldset[disabled] .btn-primary, 130 | .btn-primary.disabled:hover, 131 | .btn-primary[disabled]:hover, 132 | fieldset[disabled] .btn-primary:hover, 133 | .btn-primary.disabled:focus, 134 | .btn-primary[disabled]:focus, 135 | fieldset[disabled] .btn-primary:focus, 136 | .btn-primary.disabled.focus, 137 | .btn-primary[disabled].focus, 138 | fieldset[disabled] .btn-primary.focus, 139 | .btn-primary.disabled:active, 140 | .btn-primary[disabled]:active, 141 | fieldset[disabled] .btn-primary:active, 142 | .btn-primary.disabled.active, 143 | .btn-primary[disabled].active, 144 | fieldset[disabled] .btn-primary.active { 145 | background-color: #265a88; 146 | background-image: none; 147 | } 148 | .btn-success { 149 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 150 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 151 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 152 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 153 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 154 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 155 | background-repeat: repeat-x; 156 | border-color: #3e8f3e; 157 | } 158 | .btn-success:hover, 159 | .btn-success:focus { 160 | background-color: #419641; 161 | background-position: 0 -15px; 162 | } 163 | .btn-success:active, 164 | .btn-success.active { 165 | background-color: #419641; 166 | border-color: #3e8f3e; 167 | } 168 | .btn-success.disabled, 169 | .btn-success[disabled], 170 | fieldset[disabled] .btn-success, 171 | .btn-success.disabled:hover, 172 | .btn-success[disabled]:hover, 173 | fieldset[disabled] .btn-success:hover, 174 | .btn-success.disabled:focus, 175 | .btn-success[disabled]:focus, 176 | fieldset[disabled] .btn-success:focus, 177 | .btn-success.disabled.focus, 178 | .btn-success[disabled].focus, 179 | fieldset[disabled] .btn-success.focus, 180 | .btn-success.disabled:active, 181 | .btn-success[disabled]:active, 182 | fieldset[disabled] .btn-success:active, 183 | .btn-success.disabled.active, 184 | .btn-success[disabled].active, 185 | fieldset[disabled] .btn-success.active { 186 | background-color: #419641; 187 | background-image: none; 188 | } 189 | .btn-info { 190 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 191 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 192 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 193 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 194 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 195 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 196 | background-repeat: repeat-x; 197 | border-color: #28a4c9; 198 | } 199 | .btn-info:hover, 200 | .btn-info:focus { 201 | background-color: #2aabd2; 202 | background-position: 0 -15px; 203 | } 204 | .btn-info:active, 205 | .btn-info.active { 206 | background-color: #2aabd2; 207 | border-color: #28a4c9; 208 | } 209 | .btn-info.disabled, 210 | .btn-info[disabled], 211 | fieldset[disabled] .btn-info, 212 | .btn-info.disabled:hover, 213 | .btn-info[disabled]:hover, 214 | fieldset[disabled] .btn-info:hover, 215 | .btn-info.disabled:focus, 216 | .btn-info[disabled]:focus, 217 | fieldset[disabled] .btn-info:focus, 218 | .btn-info.disabled.focus, 219 | .btn-info[disabled].focus, 220 | fieldset[disabled] .btn-info.focus, 221 | .btn-info.disabled:active, 222 | .btn-info[disabled]:active, 223 | fieldset[disabled] .btn-info:active, 224 | .btn-info.disabled.active, 225 | .btn-info[disabled].active, 226 | fieldset[disabled] .btn-info.active { 227 | background-color: #2aabd2; 228 | background-image: none; 229 | } 230 | .btn-warning { 231 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 232 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 233 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 234 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 235 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 236 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 237 | background-repeat: repeat-x; 238 | border-color: #e38d13; 239 | } 240 | .btn-warning:hover, 241 | .btn-warning:focus { 242 | background-color: #eb9316; 243 | background-position: 0 -15px; 244 | } 245 | .btn-warning:active, 246 | .btn-warning.active { 247 | background-color: #eb9316; 248 | border-color: #e38d13; 249 | } 250 | .btn-warning.disabled, 251 | .btn-warning[disabled], 252 | fieldset[disabled] .btn-warning, 253 | .btn-warning.disabled:hover, 254 | .btn-warning[disabled]:hover, 255 | fieldset[disabled] .btn-warning:hover, 256 | .btn-warning.disabled:focus, 257 | .btn-warning[disabled]:focus, 258 | fieldset[disabled] .btn-warning:focus, 259 | .btn-warning.disabled.focus, 260 | .btn-warning[disabled].focus, 261 | fieldset[disabled] .btn-warning.focus, 262 | .btn-warning.disabled:active, 263 | .btn-warning[disabled]:active, 264 | fieldset[disabled] .btn-warning:active, 265 | .btn-warning.disabled.active, 266 | .btn-warning[disabled].active, 267 | fieldset[disabled] .btn-warning.active { 268 | background-color: #eb9316; 269 | background-image: none; 270 | } 271 | .btn-danger { 272 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 273 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 274 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 275 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 276 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 277 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 278 | background-repeat: repeat-x; 279 | border-color: #b92c28; 280 | } 281 | .btn-danger:hover, 282 | .btn-danger:focus { 283 | background-color: #c12e2a; 284 | background-position: 0 -15px; 285 | } 286 | .btn-danger:active, 287 | .btn-danger.active { 288 | background-color: #c12e2a; 289 | border-color: #b92c28; 290 | } 291 | .btn-danger.disabled, 292 | .btn-danger[disabled], 293 | fieldset[disabled] .btn-danger, 294 | .btn-danger.disabled:hover, 295 | .btn-danger[disabled]:hover, 296 | fieldset[disabled] .btn-danger:hover, 297 | .btn-danger.disabled:focus, 298 | .btn-danger[disabled]:focus, 299 | fieldset[disabled] .btn-danger:focus, 300 | .btn-danger.disabled.focus, 301 | .btn-danger[disabled].focus, 302 | fieldset[disabled] .btn-danger.focus, 303 | .btn-danger.disabled:active, 304 | .btn-danger[disabled]:active, 305 | fieldset[disabled] .btn-danger:active, 306 | .btn-danger.disabled.active, 307 | .btn-danger[disabled].active, 308 | fieldset[disabled] .btn-danger.active { 309 | background-color: #c12e2a; 310 | background-image: none; 311 | } 312 | .thumbnail, 313 | .img-thumbnail { 314 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 315 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 316 | } 317 | .dropdown-menu > li > a:hover, 318 | .dropdown-menu > li > a:focus { 319 | background-color: #e8e8e8; 320 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 321 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 322 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 323 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 324 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 325 | background-repeat: repeat-x; 326 | } 327 | .dropdown-menu > .active > a, 328 | .dropdown-menu > .active > a:hover, 329 | .dropdown-menu > .active > a:focus { 330 | background-color: #2e6da4; 331 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 332 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 333 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 334 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 336 | background-repeat: repeat-x; 337 | } 338 | .navbar-default { 339 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 340 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 342 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 344 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 345 | background-repeat: repeat-x; 346 | border-radius: 4px; 347 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 348 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 349 | } 350 | .navbar-default .navbar-nav > .open > a, 351 | .navbar-default .navbar-nav > .active > a { 352 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 353 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 354 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); 355 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); 356 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); 357 | background-repeat: repeat-x; 358 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 359 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 360 | } 361 | .navbar-brand, 362 | .navbar-nav > li > a { 363 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 364 | } 365 | .navbar-inverse { 366 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 367 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 368 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 369 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 370 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 371 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 372 | background-repeat: repeat-x; 373 | border-radius: 4px; 374 | } 375 | .navbar-inverse .navbar-nav > .open > a, 376 | .navbar-inverse .navbar-nav > .active > a { 377 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); 378 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); 379 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); 380 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); 381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); 382 | background-repeat: repeat-x; 383 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 384 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 385 | } 386 | .navbar-inverse .navbar-brand, 387 | .navbar-inverse .navbar-nav > li > a { 388 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 389 | } 390 | .navbar-static-top, 391 | .navbar-fixed-top, 392 | .navbar-fixed-bottom { 393 | border-radius: 0; 394 | } 395 | @media (max-width: 767px) { 396 | .navbar .navbar-nav .open .dropdown-menu > .active > a, 397 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, 398 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { 399 | color: #fff; 400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 405 | background-repeat: repeat-x; 406 | } 407 | } 408 | .alert { 409 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 410 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 411 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 412 | } 413 | .alert-success { 414 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 415 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 416 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 417 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 418 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 419 | background-repeat: repeat-x; 420 | border-color: #b2dba1; 421 | } 422 | .alert-info { 423 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 424 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 425 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 426 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 427 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 428 | background-repeat: repeat-x; 429 | border-color: #9acfea; 430 | } 431 | .alert-warning { 432 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 433 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 435 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 437 | background-repeat: repeat-x; 438 | border-color: #f5e79e; 439 | } 440 | .alert-danger { 441 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 442 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 443 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 444 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 445 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 446 | background-repeat: repeat-x; 447 | border-color: #dca7a7; 448 | } 449 | .progress { 450 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 451 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 453 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 455 | background-repeat: repeat-x; 456 | } 457 | .progress-bar { 458 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); 459 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); 460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); 461 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); 462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); 463 | background-repeat: repeat-x; 464 | } 465 | .progress-bar-success { 466 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 467 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 469 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 471 | background-repeat: repeat-x; 472 | } 473 | .progress-bar-info { 474 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 475 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 476 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 477 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 478 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 479 | background-repeat: repeat-x; 480 | } 481 | .progress-bar-warning { 482 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 483 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 484 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 485 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 486 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 487 | background-repeat: repeat-x; 488 | } 489 | .progress-bar-danger { 490 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 491 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 492 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 493 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 494 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 495 | background-repeat: repeat-x; 496 | } 497 | .progress-bar-striped { 498 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 499 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 500 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 501 | } 502 | .list-group { 503 | border-radius: 4px; 504 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 505 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 506 | } 507 | .list-group-item.active, 508 | .list-group-item.active:hover, 509 | .list-group-item.active:focus { 510 | text-shadow: 0 -1px 0 #286090; 511 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); 512 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); 513 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); 514 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); 515 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); 516 | background-repeat: repeat-x; 517 | border-color: #2b669a; 518 | } 519 | .list-group-item.active .badge, 520 | .list-group-item.active:hover .badge, 521 | .list-group-item.active:focus .badge { 522 | text-shadow: none; 523 | } 524 | .panel { 525 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 526 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 527 | } 528 | .panel-default > .panel-heading { 529 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 530 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 531 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 532 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 533 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 534 | background-repeat: repeat-x; 535 | } 536 | .panel-primary > .panel-heading { 537 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 538 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 539 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 540 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 541 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 542 | background-repeat: repeat-x; 543 | } 544 | .panel-success > .panel-heading { 545 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 546 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 547 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 548 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 549 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 550 | background-repeat: repeat-x; 551 | } 552 | .panel-info > .panel-heading { 553 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 554 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 555 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 556 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 557 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 558 | background-repeat: repeat-x; 559 | } 560 | .panel-warning > .panel-heading { 561 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 562 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 563 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 564 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 565 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 566 | background-repeat: repeat-x; 567 | } 568 | .panel-danger > .panel-heading { 569 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 570 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 571 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 572 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 573 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 574 | background-repeat: repeat-x; 575 | } 576 | .well { 577 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 578 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 579 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 580 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 581 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 582 | background-repeat: repeat-x; 583 | border-color: #dcdcdc; 584 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 585 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 586 | } 587 | /*# sourceMappingURL=bootstrap-theme.css.map */ 588 | -------------------------------------------------------------------------------- /samples/vendor/bootstrap/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.6 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} 6 | /*# sourceMappingURL=bootstrap-theme.min.css.map */ -------------------------------------------------------------------------------- /samples/vendor/bootstrap/css/bootstrap-theme.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA"} -------------------------------------------------------------------------------- /samples/vendor/bootstrap/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.6 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>2)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.6",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.6",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.6",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.6",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.6",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.6",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.6",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.6",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); -------------------------------------------------------------------------------- /samples/yesno/yesno.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/yesno/yesnocontroller.js: -------------------------------------------------------------------------------- 1 | var app = angular.module('sampleapp'); 2 | 3 | app.controller('YesNoController', ['$scope', 'close', function($scope, close) { 4 | 5 | $scope.close = function(result) { 6 | close(result, 500); // close, but give 500ms for bootstrap to animate 7 | }; 8 | 9 | }]); -------------------------------------------------------------------------------- /src/angular-modal-service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var module = angular.module('angularModalService', []); 4 | 5 | module.provider('ModalService', function ModalServiceProvider() { 6 | var _options = { 7 | closeDelay: 0 8 | }; 9 | 10 | this.configureOptions = function (config) { 11 | angular.extend(_options, config); 12 | }; 13 | this.$get = ['$animate', '$document', '$compile', '$controller', '$http', '$rootScope', '$q', '$templateRequest', '$timeout', 14 | function ($animate, $document, $compile, $controller, $http, $rootScope, $q, $templateRequest, $timeout) { 15 | 16 | function ModalService(configOptions) { 17 | 18 | var self = this; 19 | 20 | //stash configOptions 21 | self.configOptions = configOptions; 22 | 23 | // Track open modals. 24 | self.openModals = []; 25 | 26 | // Returns a promise which gets the template, either 27 | // from the template parameter or via a request to the 28 | // template url parameter. 29 | var getTemplate = function (template, templateUrl) { 30 | var deferred = $q.defer(); 31 | if (template) { 32 | deferred.resolve(template); 33 | } else if (templateUrl) { 34 | $templateRequest(templateUrl, true) 35 | .then(function (template) { 36 | deferred.resolve(template); 37 | }, function (error) { 38 | deferred.reject(error); 39 | }); 40 | } else { 41 | deferred.reject("No template or templateUrl has been specified."); 42 | } 43 | return deferred.promise; 44 | }; 45 | 46 | // Adds an element to the DOM as the last child of its container 47 | // like append, but uses $animate to handle animations. Returns a 48 | // promise that is resolved once all animation is complete. 49 | var appendChild = function (parent, child) { 50 | var children = parent.children(); 51 | if (children.length > 0) { 52 | return $animate.enter(child, parent, children[children.length - 1]); 53 | } 54 | return $animate.enter(child, parent); 55 | }; 56 | 57 | // Close all modals, providing the given result to the close promise. 58 | self.closeModals = function (result, delay) { 59 | delay = delay || self.configOptions.closeDelay; 60 | while (self.openModals.length) { 61 | self.openModals[0].close(result, delay); 62 | self.openModals.splice(0, 1); 63 | } 64 | }; 65 | 66 | /* 67 | * Creates a controller with scope bindings 68 | */ 69 | var buildComponentController = function(options) { 70 | return ['$scope', 'close', function($scope, close) { 71 | $scope.close = close; 72 | $scope.bindings = options.bindings; 73 | }]; 74 | }; 75 | 76 | /* 77 | * Creates a component template 78 | * 79 | * Input: 80 | * 81 | * { 82 | * component: 'myComponent', 83 | * bindings: { 84 | * name: 'Foo', 85 | * phoneNumber: '123-456-7890' 86 | * } 87 | * } 88 | * 89 | * Output: 90 | * 91 | * '' 92 | */ 93 | var buildComponentTemplate = function(options) { 94 | var kebabCase = function(camelCase) { 95 | var skewer = function(_m, c1, c2) { return [c1, c2].join('-').toLowerCase(); }; 96 | return camelCase.replace(/([a-z0-9])([A-Z])/g, skewer); 97 | }; 98 | 99 | var componentHandle = kebabCase(options.component); 100 | var template = '<' + componentHandle + ' close="close"'; 101 | var inputKeys = Object.keys(options.bindings || {}) 102 | if (inputKeys.length > 0) { 103 | var bindingAttributes = inputKeys.map(function(inputKey) { 104 | return kebabCase(inputKey) + '="bindings.' + inputKey + '"'; 105 | }); 106 | template += ' ' + bindingAttributes.join(' '); 107 | } 108 | template += '>'; 109 | 110 | return template; 111 | }; 112 | 113 | var setupComponentOptions = function(options) { 114 | options.controller = buildComponentController(options); 115 | options.template = buildComponentTemplate(options); 116 | }; 117 | 118 | self.showModal = function (options) { 119 | if (options.component) { 120 | setupComponentOptions(options); 121 | } 122 | 123 | // Get the body of the document, we'll add the modal to this. 124 | var body = angular.element($document[0].body); 125 | 126 | // Create a deferred we'll resolve when the modal is ready. 127 | var deferred = $q.defer(); 128 | 129 | // Validate the input parameters. 130 | var controllerName = options.controller; 131 | if (!controllerName) { 132 | deferred.reject("No controller has been specified."); 133 | return deferred.promise; 134 | } 135 | 136 | // Get the actual html of the template. 137 | getTemplate(options.template, options.templateUrl) 138 | .then(function (template) { 139 | 140 | // The main modal object we will build. 141 | var modal = {}; 142 | 143 | // Create a new scope for the modal. 144 | var modalScope = (options.scope || $rootScope).$new(), 145 | rootScopeOnClose = null, 146 | locationChangeSuccess = options.locationChangeSuccess; 147 | 148 | // Allow locationChangeSuccess event registration to be configurable. 149 | // True (default) = event registered with defaultCloseDelay 150 | // # (greater than 0) = event registered with delay 151 | // False = disabled 152 | if (locationChangeSuccess === false) { 153 | rootScopeOnClose = angular.noop; 154 | } 155 | else if (angular.isNumber(locationChangeSuccess) && locationChangeSuccess >= 0) { 156 | $timeout(function () { 157 | rootScopeOnClose = $rootScope.$on('$locationChangeSuccess', inputs.close); 158 | }, locationChangeSuccess); 159 | } 160 | else { 161 | $timeout(function () { 162 | rootScopeOnClose = $rootScope.$on('$locationChangeSuccess', inputs.close); 163 | }, self.configOptions.closeDelay); 164 | } 165 | 166 | 167 | // Create the inputs object to the controller - this will include 168 | // the scope, as well as all inputs provided. 169 | // We will also create a deferred that is resolved with a provided 170 | // close function. The controller can then call 'close(result)'. 171 | // The controller can also provide a delay for closing - this is 172 | // helpful if there are closing animations which must finish first. 173 | var closeDeferred = $q.defer(); 174 | var closedDeferred = $q.defer(); 175 | var hasAlreadyBeenClosed = false; 176 | 177 | var inputs = { 178 | $scope: modalScope, 179 | close: function (result, delay) { 180 | if (hasAlreadyBeenClosed) { 181 | return; 182 | } 183 | hasAlreadyBeenClosed = true; 184 | 185 | delay = delay || self.configOptions.closeDelay; 186 | // If we have a pre-close function, call it. 187 | if (typeof options.preClose === 'function') options.preClose(modal, result, delay); 188 | 189 | if (delay === undefined || delay === null) delay = 0; 190 | 191 | $timeout(function () { 192 | 193 | cleanUpClose(result); 194 | 195 | }, delay); 196 | } 197 | }; 198 | 199 | // If we have provided any inputs, pass them to the controller. 200 | if (options.inputs) angular.extend(inputs, options.inputs); 201 | 202 | // Compile then link the template element, building the actual element. 203 | // Set the $element on the inputs so that it can be injected if required. 204 | var linkFn = $compile(template); 205 | var modalElement = linkFn(modalScope); 206 | inputs.$element = modalElement; 207 | 208 | // Create the controller, explicitly specifying the scope to use. 209 | var controllerObjBefore = modalScope[options.controllerAs]; 210 | var modalController = $controller(options.controller, inputs, false, options.controllerAs); 211 | 212 | if (options.controllerAs && controllerObjBefore) { 213 | angular.extend(modalController, controllerObjBefore); 214 | } 215 | 216 | // Then, append the modal to the dom. 217 | var appendTarget = body; // append to body when no custom append element is specified 218 | if (angular.isString(options.appendElement)) { 219 | // query the document for the first element that matches the selector 220 | // and create an angular element out of it. 221 | appendTarget = angular.element($document[0].querySelector(options.appendElement)); 222 | 223 | } else if (options.appendElement) { 224 | // append to custom append element 225 | appendTarget = options.appendElement; 226 | } 227 | 228 | appendChild(appendTarget, modalElement); 229 | 230 | // Finally, append any custom classes to the body 231 | if (options.bodyClass) { 232 | body[0].classList.add(options.bodyClass); 233 | } 234 | 235 | // Populate the modal object... 236 | modal.controller = modalController; 237 | modal.scope = modalScope; 238 | modal.element = modalElement; 239 | modal.close = closeDeferred.promise; 240 | modal.closed = closedDeferred.promise; 241 | 242 | // $onInit is part of the component lifecycle introduced in AngularJS 1.6.x 243 | // Because it may not be defined on all controllers, 244 | // we must check for it before attempting to invoke it. 245 | // https://docs.angularjs.org/guide/component#component-based-application-architecture 246 | if (angular.isFunction(modal.controller.$onInit)) { 247 | modal.controller.$onInit(); 248 | } 249 | 250 | // ...which is passed to the caller via the promise. 251 | deferred.resolve(modal); 252 | 253 | // Clear previous input focus to avoid open multiple modals on enter 254 | document.activeElement.blur(); 255 | 256 | // We can track this modal in our open modals. 257 | self.openModals.push({modal: modal, close: inputs.close}); 258 | 259 | function cleanUpClose(result) { 260 | 261 | // Resolve the 'close' promise. 262 | closeDeferred.resolve(result); 263 | 264 | // Remove the custom class from the body 265 | if (options.bodyClass) { 266 | body[0].classList.remove(options.bodyClass); 267 | } 268 | 269 | // Let angular remove the element and wait for animations to finish. 270 | $animate.leave(modalElement) 271 | .then(function () { 272 | // prevent error if modal is already destroyed 273 | if (!modalElement) { 274 | return; 275 | } 276 | 277 | // Resolve the 'closed' promise. 278 | closedDeferred.resolve(result); 279 | 280 | // We can now clean up the scope 281 | modalScope.$destroy(); 282 | 283 | // Remove the modal from the set of open modals. 284 | for (var i = 0; i < self.openModals.length; i++) { 285 | if (self.openModals[i].modal === modal) { 286 | self.openModals.splice(i, 1); 287 | break; 288 | } 289 | } 290 | 291 | // Unless we null out all of these objects we seem to suffer 292 | // from memory leaks, if anyone can explain why then I'd 293 | // be very interested to know. 294 | inputs.close = null; 295 | deferred = null; 296 | closeDeferred = null; 297 | modal = null; 298 | inputs = null; 299 | modalElement = null; 300 | modalScope = null; 301 | 302 | }); 303 | // remove event watcher 304 | rootScopeOnClose && rootScopeOnClose(); 305 | } 306 | 307 | }) 308 | .then(null, function (error) { // 'catch' doesn't work in IE8. 309 | deferred.reject(error); 310 | }); 311 | 312 | return deferred.promise; 313 | }; 314 | 315 | } 316 | 317 | return new ModalService(_options); 318 | }]; 319 | }); 320 | -------------------------------------------------------------------------------- /test/basics.spec.js: -------------------------------------------------------------------------------- 1 | describe('basics', function() { 2 | 3 | var ModalService = null; 4 | 5 | beforeEach(() => { 6 | angular.mock.module('angularModalService'); 7 | inject((_ModalService_) => { 8 | ModalService = _ModalService_; 9 | }); 10 | }); 11 | 12 | it('should be able to inject the modal service', () => { 13 | expect(ModalService).not.to.equal(null); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /test/bodyClass.spec.js: -------------------------------------------------------------------------------- 1 | describe('bodyclass', () => { 2 | 3 | let ModalService = null; 4 | let $httpBackend = null; 5 | let $timeout = null; 6 | let $document = null; 7 | 8 | angular.module('bodyclasstests', ['angularModalService']) 9 | .controller('BodyClassController', ($scope, close) => { 10 | $scope.close = close; 11 | }); 12 | 13 | beforeEach(() => { 14 | angular.mock.module('bodyclasstests'); 15 | inject((_ModalService_, $injector) => { 16 | ModalService = _ModalService_; 17 | $httpBackend = $injector.get('$httpBackend'); 18 | $timeout = $injector.get('$timeout'); 19 | $document = $injector.get('$document'); 20 | $httpBackend.when('GET', 'some/template1.html').respond("
template
"); 21 | }); 22 | }); 23 | 24 | afterEach(() => { 25 | $httpBackend.verifyNoOutstandingExpectation(); 26 | $httpBackend.verifyNoOutstandingRequest(); 27 | }); 28 | 29 | it('should add the specified class to the body', () => { 30 | 31 | $httpBackend.expectGET('some/template1.html'); 32 | 33 | ModalService.showModal({ 34 | controller: "BodyClassController", 35 | templateUrl: "some/template1.html", 36 | bodyClass: "custom-class" 37 | }).then((modal) => { 38 | // We should be able to find the custom class on the body. 39 | expect($document.find('body')[0].classList.contains('custom-class')).to.equal(true); 40 | }); 41 | 42 | $httpBackend.flush(); 43 | 44 | }); 45 | 46 | it('should remove the specified class from the body on close', () => { 47 | $httpBackend.expectGET('some/template1.html'); 48 | 49 | ModalService.showModal({ 50 | controller: "BodyClassController", 51 | templateUrl: "some/template1.html", 52 | bodyClass: "custom-class" 53 | }).then(modal => { 54 | 55 | // We should be able to find the custom class on the body. 56 | expect($document.find('body')[0].classList.contains('custom-class')).to.equal(true); 57 | 58 | modal.close.then(result => { 59 | // The custom class should have been removed after closing. 60 | expect($document.find('body')[0].classList.contains('custom-class')).to.equal(false); 61 | }); 62 | 63 | modal.scope.close(); 64 | }); 65 | 66 | $httpBackend.flush(); 67 | $timeout.flush(); 68 | }); 69 | 70 | }); 71 | -------------------------------------------------------------------------------- /test/close.spec.js: -------------------------------------------------------------------------------- 1 | let sinon = require('sinon'); 2 | 3 | describe('close callback', () => { 4 | let ModalService = null; 5 | let $timeout = null; 6 | let $rootScope = null; 7 | let options; 8 | let modal; 9 | 10 | angular.module('close', ['angularModalService']); 11 | angular.module('close').controller('CloseController', 12 | function($scope, close) { 13 | $scope.close = close; 14 | } 15 | ); 16 | 17 | beforeEach(() => { 18 | angular.mock.module('close'); 19 | 20 | inject((_ModalService_, _$timeout_, _$rootScope_) => { 21 | ModalService = _ModalService_; 22 | $timeout = _$timeout_; 23 | $rootScope = _$rootScope_; 24 | }); 25 | 26 | options = { 27 | controller: 'CloseController', 28 | template: '
Hi
', 29 | preClose: sinon.spy() 30 | }; 31 | 32 | ModalService.showModal(options); 33 | $rootScope.$apply(); 34 | 35 | modal = ModalService.openModals[0]; 36 | }); 37 | 38 | describe('when the modal\'s close function is called twice', () => { 39 | it('should only perform the close callback once', function() { 40 | modal.close(); 41 | modal.close(); 42 | 43 | modal.modal.close.then(() => { 44 | sinon.assert.calledOnce(options.preClose); 45 | }); 46 | 47 | $rootScope.$apply(); 48 | }); 49 | }); 50 | 51 | describe('when the modal\'s close function is called after a state change', () => { 52 | it('should only perform the close callback once', function() { 53 | $rootScope.$broadcast('$locationChangeSuccess'); 54 | modal.close(); 55 | 56 | modal.modal.close.then(() => { 57 | sinon.assert.calledOnce(options.preClose); 58 | }); 59 | 60 | $rootScope.$apply(); 61 | }); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /test/closeModals.spec.js: -------------------------------------------------------------------------------- 1 | describe('closeAll', () => { 2 | 3 | var ModalService = null; 4 | var $httpBackend = null; 5 | var $timeout = null; 6 | var $q = null; 7 | var $rootScope = null; 8 | 9 | angular.module('closemodals', ['angularModalService']) 10 | .controller('Modal1Controller', ($scope, close) => { 11 | $scope.close = close; 12 | }) 13 | .controller('Modal2Controller', ($scope, close) => { 14 | $scope.close = close; 15 | }); 16 | 17 | beforeEach(() => { 18 | angular.mock.module('closemodals'); 19 | inject((_ModalService_, $injector, _$rootScope_, _$q_) => { 20 | ModalService = _ModalService_; 21 | $timeout = $injector.get('$timeout'); 22 | $rootScope = _$rootScope_; 23 | $q = _$q_; 24 | }); 25 | }); 26 | 27 | it('should close both modals when \'closeModals\' is called', () => { 28 | 29 | // Open one modal. 30 | ModalService.showModal({ 31 | controller: "Modal1Controller", 32 | template: "
Modal 1
" 33 | }).then((modal) => { 34 | modal.close.then((result) => { expect(result).to.equal('closeModals'); }); 35 | }).then(() => { 36 | // Open another... 37 | ModalService.showModal({ 38 | controller: "Modal2Controller", 39 | template: "
Modal 2
" 40 | }).then((modal) => { 41 | modal.close.then((result) => { expect(result).to.equal('closeModals'); }); 42 | }); 43 | }).then(() => { 44 | // Then close them both... 45 | ModalService.closeModals('closeModals'); 46 | }); 47 | 48 | $timeout.flush(); 49 | 50 | }); 51 | 52 | }); 53 | -------------------------------------------------------------------------------- /test/component.spec.js: -------------------------------------------------------------------------------- 1 | describe('component', () => { 2 | let ModalService; 3 | let $rootScope; 4 | 5 | let modalOptions; 6 | let component; 7 | 8 | let testModule = angular.module('component-tests', ['angularModalService']); 9 | testModule.component('myComponent', { 10 | template: 'test-template', 11 | bindings: { 12 | name: '<', 13 | someRecord: ' { 22 | angular.mock.module('component-tests'); 23 | inject((_ModalService_, $injector) => { 24 | ModalService = _ModalService_; 25 | $rootScope = $injector.get('$rootScope'); 26 | 27 | modalOptions = { 28 | component: 'myComponent', 29 | bindings: { 30 | name: 'MyName', 31 | myRecord: { id: '123' } 32 | } 33 | }; 34 | 35 | ModalService.showModal(modalOptions); 36 | $rootScope.$apply(); 37 | }); 38 | }); 39 | 40 | it('binds the provided bindings to the requested component', () => { 41 | expect(component.name).to.equal('MyName'); 42 | expect(component.someRecord).to.equal(modalOptions.bindings.myRecord); 43 | }); 44 | 45 | it('binds the close function to the component', () => { 46 | expect(component.close).to.equal(ModalService.openModals[0].close); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/configOptions.spec.js: -------------------------------------------------------------------------------- 1 | describe('ModalServiceProvider', function() { 2 | 'use strict'; 3 | 4 | // Provider instance 5 | var ModalService; 6 | 7 | // Instantiates the module 8 | beforeEach(function() { 9 | angular.mock.module('angularModalService'); 10 | }); 11 | 12 | // Here we don't do any configuration to our provider 13 | describe('Default Configuration', function() { 14 | 15 | beforeEach(function() { 16 | inject(function(_ModalService_) { 17 | ModalService = _ModalService_; 18 | }); 19 | }); 20 | 21 | it('Should get the default value', function() { 22 | expect(ModalService.configOptions.closeDelay).to.equal(0); 23 | }); 24 | 25 | }); 26 | 27 | // Here we do some configuration 28 | describe('Configuration A', function() { 29 | 30 | // Configure the provider and instantiate it 31 | beforeEach(function() { 32 | angular.mock.module(function(ModalServiceProvider) { 33 | ModalServiceProvider.configureOptions({closeDelay: 500}); 34 | }); 35 | 36 | inject(function(_ModalService_) { 37 | ModalService = _ModalService_; 38 | }); 39 | }); 40 | 41 | it('Should get the configured value', function() { 42 | expect(ModalService.configOptions.closeDelay).to.equal(500); 43 | }); 44 | 45 | }); 46 | 47 | }); -------------------------------------------------------------------------------- /test/controller.spec.js: -------------------------------------------------------------------------------- 1 | var sinon = require('sinon'); 2 | 3 | describe('controller', () => { 4 | 5 | var ModalService = null; 6 | var $httpBackend = null; 7 | var $timeout = null; 8 | 9 | angular.module('controllertests', ['angularModalService']) 10 | .controller('CloseController', ($scope, close) => { 11 | $scope.close = close; 12 | }) 13 | .controller('InputsController', ($scope, input1, input2, close) => { 14 | $scope.input1 = input1; 15 | $scope.input2 = input2; 16 | $scope.close = close; 17 | }) 18 | // Important: 'controllerAs' functions are bound to 'this', this is very 19 | // important, we CANNOT change the below to an arrow function. 20 | .controller('ControllerAsController', function() { 21 | var vm = this; 22 | vm.character = "Fry"; 23 | vm.checkValidity = () => { 24 | return vm.ExampleForm.$valid; 25 | } 26 | }) 27 | .controller('ElementController', ($scope, $element) => { 28 | $scope.getElement = () => { return $element; }; 29 | }) 30 | .controller('OnInitController', function() { 31 | this.$onInit = sinon.spy(); 32 | }); 33 | 34 | beforeEach(() => { 35 | angular.mock.module('controllertests'); 36 | inject((_ModalService_, $injector) => { 37 | ModalService = _ModalService_; 38 | $httpBackend = $injector.get('$httpBackend'); 39 | $timeout = $injector.get('$timeout'); 40 | $httpBackend.when('GET', 'some/controllertemplate.html').respond("
controller template
"); 41 | $httpBackend.when('GET', 'some/formtemplate.html').respond( 42 | "
" 43 | ); 44 | }); 45 | }); 46 | 47 | afterEach(() => { 48 | $httpBackend.verifyNoOutstandingExpectation(); 49 | $httpBackend.verifyNoOutstandingRequest(); 50 | }); 51 | 52 | it('should inject the close function into the controller', () => { 53 | 54 | $httpBackend.expectGET('some/controllertemplate.html'); 55 | 56 | ModalService.showModal({ 57 | controller: "CloseController", 58 | templateUrl: "some/controllertemplate.html" 59 | }).then((modal) => { 60 | 61 | // The controller we've created should put the close function on 62 | // the scope, this is how we test it's been passed. 63 | expect(modal.scope.close).not.to.equal(undefined); 64 | 65 | }); 66 | 67 | $httpBackend.flush(); 68 | 69 | }); 70 | 71 | it('should inject inputs to the controller', () => { 72 | 73 | $httpBackend.expectGET('some/controllertemplate.html'); 74 | 75 | ModalService.showModal({ 76 | controller: "InputsController", 77 | templateUrl: "some/controllertemplate.html", 78 | inputs: { 79 | input1: 15, 80 | input2: "hi" 81 | } 82 | }).then((modal) => { 83 | 84 | // The controller sets the inputs on the scope. 85 | expect(modal.scope.input1).to.equal(15); 86 | expect(modal.scope.input2).to.equal("hi"); 87 | 88 | }); 89 | 90 | $httpBackend.flush(); 91 | 92 | }); 93 | 94 | it('should add a controller to the scope if controllerAs is used', () => { 95 | 96 | $httpBackend.expectGET('some/controllertemplate.html'); 97 | 98 | ModalService.showModal({ 99 | controller: 'ControllerAsController', 100 | controllerAs: 'futurama', 101 | templateUrl: 'some/controllertemplate.html' 102 | }).then((modal) => { 103 | 104 | // The controller should be on the scope. 105 | expect(modal.scope.futurama).not.to.equal(null); 106 | 107 | // Fields defined on the controller instance should be on the 108 | // controller on the scope. 109 | expect(modal.scope.futurama.character).to.equal('Fry'); 110 | 111 | }); 112 | 113 | $httpBackend.flush(); 114 | 115 | }); 116 | 117 | it('should add a controller to the scope if the controller is inlined', () => { 118 | 119 | $httpBackend.expectGET('some/controllertemplate.html'); 120 | 121 | ModalService.showModal({ 122 | controller: ($scope) => { 123 | $scope.character = "Fry"; 124 | }, 125 | templateUrl: 'some/controllertemplate.html' 126 | }).then((modal) => { 127 | 128 | 129 | expect(modal.scope).not.to.equal(null); 130 | expect(modal.scope.character).to.equal('Fry'); 131 | 132 | }); 133 | 134 | $httpBackend.flush(); 135 | 136 | }); 137 | 138 | it('should add a controller to the scope if the controller is inlined with controllerAs', () => { 139 | 140 | $httpBackend.expectGET('some/controllertemplate.html'); 141 | 142 | ModalService.showModal({ 143 | controller: function() { 144 | this.character = "Fry"; 145 | }, 146 | controllerAs: 'futurama', 147 | templateUrl: 'some/controllertemplate.html' 148 | }).then((modal) => { 149 | 150 | // The controller should be on the scope. 151 | expect(modal.scope.futurama).not.to.equal(null); 152 | 153 | // Fields defined on the controller instance should be on the 154 | // controller on the scope. 155 | expect(modal.scope.futurama.character).to.equal('Fry'); 156 | 157 | }); 158 | 159 | $httpBackend.flush(); 160 | 161 | }); 162 | 163 | 164 | it('should add a controller to the scope if the controller is inlined with controllerAs and also annotated', () => { 165 | 166 | $httpBackend.expectGET('some/controllertemplate.html'); 167 | 168 | ModalService.showModal({ 169 | controller: ['$http', function ($http) { 170 | expect($http).not.to.equal(null); 171 | this.character = "Fry"; 172 | }], 173 | controllerAs: 'futurama', 174 | templateUrl: 'some/controllertemplate.html' 175 | }).then((modal) => { 176 | 177 | // The controller should be on the scope. 178 | expect(modal.scope.futurama).not.to.equal(null); 179 | 180 | // Fields defined on the controller instance should be on the 181 | // controller on the scope. 182 | expect(modal.scope.futurama.character).to.equal('Fry'); 183 | 184 | }); 185 | 186 | $httpBackend.flush(); 187 | 188 | }); 189 | 190 | it('should inject the modal element into the controller', () => { 191 | 192 | $httpBackend.expectGET('some/controllertemplate.html'); 193 | 194 | ModalService.showModal({ 195 | controller: 'ElementController', 196 | templateUrl: 'some/controllertemplate.html' 197 | }).then((modal) => { 198 | 199 | // The controller should be on the scope. 200 | expect(modal.scope.getElement()).not.to.equal(undefined); 201 | 202 | }); 203 | 204 | $httpBackend.flush(); 205 | 206 | }); 207 | 208 | it('should correct process form with controllerAs.form syntax', () => { 209 | 210 | $httpBackend.expectGET('some/formtemplate.html'); 211 | 212 | ModalService.showModal({ 213 | controller: 'ControllerAsController', 214 | controllerAs: 'formCtrl', 215 | templateUrl: 'some/formtemplate.html' 216 | }).then((modal) => { 217 | expect(modal.scope.formCtrl.ExampleForm).not.to.equal(undefined); 218 | expect(modal.scope.formCtrl.checkValidity()).to.equal(true); 219 | }); 220 | 221 | $httpBackend.flush(); 222 | 223 | }); 224 | 225 | it('should run the controller\'s $onInit function if specified', () => { 226 | $httpBackend.expectGET('some/controllertemplate.html'); 227 | 228 | ModalService.showModal({ 229 | controller: 'OnInitController', 230 | templateUrl: 'some/controllertemplate.html' 231 | }).then((modal) => { 232 | sinon.assert.calledOnce(modal.controller.$onInit); 233 | }); 234 | 235 | $httpBackend.flush(); 236 | }); 237 | }); 238 | -------------------------------------------------------------------------------- /test/dom.spec.js: -------------------------------------------------------------------------------- 1 | describe('dom', () => { 2 | 3 | let ModalService = null; 4 | let $httpBackend = null; 5 | let $timeout = null; 6 | let $rootScope = null; 7 | 8 | angular.module('domtests', ['angularModalService']) 9 | .controller('DomController', ($scope, close) => { 10 | $scope.close = close; 11 | }); 12 | 13 | beforeEach(() => { 14 | angular.mock.module('domtests'); 15 | inject((_ModalService_, _$rootScope_, $injector) => { 16 | ModalService = _ModalService_; 17 | $httpBackend = $injector.get('$httpBackend'); 18 | $rootScope = _$rootScope_; 19 | $timeout = $injector.get('$timeout'); 20 | $httpBackend.when('GET', 'some/template1.html').respond("
template1
"); 21 | $httpBackend.when('GET', 'some/template2.html').respond("
template2
"); 22 | }); 23 | }); 24 | 25 | afterEach(() => { 26 | $httpBackend.verifyNoOutstandingExpectation(); 27 | $httpBackend.verifyNoOutstandingRequest(); 28 | }); 29 | 30 | it('should add the template html to the dom', () => { 31 | 32 | $httpBackend.expectGET('some/template1.html'); 33 | 34 | ModalService.showModal({ 35 | controller: "DomController", 36 | templateUrl: "some/template1.html" 37 | }).then((modal) => { 38 | 39 | // We should be able to find the element that has been created in the dom. 40 | expect(document.getElementById('template1')).not.to.equal(null); 41 | 42 | }); 43 | 44 | $httpBackend.flush(); 45 | 46 | }); 47 | 48 | it('should add the template html to the custom dom element', () => { 49 | $httpBackend.expectGET('some/template1.html'); 50 | 51 | // create fake element 52 | let fakeDomElement = document.createElement('div'); 53 | fakeDomElement.id = 'fake-dom-element'; 54 | 55 | // insert fakeDomElement into the document to test against 56 | document.body.insertBefore(fakeDomElement, null); 57 | 58 | ModalService.showModal({ 59 | controller: "DomController", 60 | templateUrl: "some/template1.html", 61 | appendElement: angular.element(document.getElementById('fake-dom-element')) 62 | }).then((modal) => { 63 | // We should be able to find the element that has been created in the custom dom element 64 | expect(angular.element(document.querySelector('#fake-dom-element')).find('div')).not.to.equal(null); 65 | }); 66 | 67 | $httpBackend.flush(); 68 | }); 69 | 70 | it('should add the template html to the custom selector', () => { 71 | $httpBackend.expectGET('some/template1.html'); 72 | 73 | // create fake element 74 | let fakeDomElement = document.createElement('div'); 75 | fakeDomElement.id = 'fake-dom-element'; 76 | 77 | // insert fakeDomElement into the document to test against 78 | document.body.insertBefore(fakeDomElement, null); 79 | 80 | ModalService.showModal({ 81 | controller: "DomController", 82 | templateUrl: "some/template1.html", 83 | appendElement: '#fake-dom-element' 84 | }).then((modal) => { 85 | // We should be able to find the element that has been created in the custom dom element 86 | expect(angular.element(document.querySelector('#fake-dom-element')).find('div')).not.to.equal(null); 87 | }); 88 | 89 | $httpBackend.flush(); 90 | }); 91 | 92 | it('should close the template html to the custom dom element', () => { 93 | $httpBackend.expectGET('some/template1.html'); 94 | 95 | // create fake element 96 | let fakeDomElement = document.createElement('div'); 97 | fakeDomElement.id = 'fake-dom-element'; 98 | 99 | // insert fakeDomElement into the document to test against 100 | document.body.insertBefore(fakeDomElement, null); 101 | 102 | ModalService.showModal({ 103 | controller: "DomController", 104 | templateUrl: "some/template1.html", 105 | appendElement: angular.element(document.getElementById('fake-dom-element')) 106 | }).then((modal) => { 107 | // We should be able to find the element that has been created in the custom dom element 108 | expect(angular.element(document.querySelector('#fake-dom-element')).find('div')).not.to.equal(null); 109 | 110 | modal.close.then((result) => { 111 | expect(document.getElementById('template2')).to.equal(null); 112 | }); 113 | 114 | modal.scope.close(); 115 | }); 116 | 117 | $httpBackend.flush(); 118 | $timeout.flush(); 119 | }); 120 | 121 | it('should remove the template html from the dom when the controller closes the modal', () => { 122 | 123 | $httpBackend.expectGET('some/template2.html'); 124 | 125 | ModalService.showModal({ 126 | controller: "DomController", 127 | templateUrl: "some/template2.html" 128 | }).then((modal) => { 129 | 130 | // We should be able to find the element that has been created in the dom. 131 | expect(document.getElementById('template2')).not.to.equal(null); 132 | 133 | modal.closed.then((result) => { 134 | expect(document.getElementById('template2')).to.equal(null); 135 | }); 136 | 137 | modal.scope.close(); 138 | }); 139 | 140 | $httpBackend.flush(); 141 | $timeout.flush(); 142 | 143 | }); 144 | 145 | it('should remove the template html from the dom when the $locationChangeSuccess event is fired', () => { 146 | 147 | $httpBackend.expectGET('some/template2.html'); 148 | 149 | ModalService.showModal({ 150 | controller: "DomController", 151 | templateUrl: "some/template2.html" 152 | }).then((modal) => { 153 | 154 | // We should be able to find the element that has been created in the dom. 155 | expect(document.getElementById('template2')).not.to.equal(null); 156 | 157 | modal.close.then((result) => { 158 | expect(document.getElementById('template2')).to.equal(null); 159 | }); 160 | 161 | $rootScope.$emit('$locationChangeSuccess'); 162 | }); 163 | 164 | $httpBackend.flush(); 165 | $timeout.flush(); 166 | 167 | }); 168 | 169 | it('should leave the template html in the dom when the $locationChangeSuccess event is explicitly enabled', () => { 170 | 171 | $httpBackend.expectGET('some/template2.html'); 172 | 173 | ModalService.showModal({ 174 | controller: "DomController", 175 | templateUrl: "some/template2.html" 176 | }).then((modal) => { 177 | 178 | // We should be able to find the element that has been created in the dom. 179 | expect(document.getElementById('template2')).not.to.equal(null); 180 | 181 | modal.close.then((result) => { 182 | expect(document.getElementById('template2')).to.equal(null); 183 | }); 184 | 185 | $rootScope.$emit('$locationChangeSuccess'); 186 | }); 187 | 188 | $httpBackend.flush(); 189 | $timeout.flush(); 190 | 191 | }); 192 | 193 | it('should leave the template html in the dom when the $locationChangeSuccess event is explicitly disabled', (done) => { 194 | 195 | $httpBackend.expectGET('some/template2.html'); 196 | 197 | ModalService.showModal({ 198 | controller: "DomController", 199 | templateUrl: "some/template2.html", 200 | locationChangeSuccess : false 201 | }).then((modal) => { 202 | 203 | // We should be able to find the element that has been created in the dom. 204 | expect(document.getElementById('template2')).not.to.equal(null); 205 | 206 | $rootScope.$emit('$locationChangeSuccess'); 207 | 208 | setTimeout(() => { 209 | expect(document.getElementById('template2')).not.to.equal(null); 210 | done(); 211 | }, 3); 212 | }); 213 | 214 | $httpBackend.flush(); 215 | $timeout.flush(); 216 | 217 | }); 218 | 219 | it('should leave the template html in the dom when the $locationChangeSuccess event for the specified delay', () => { 220 | 221 | $httpBackend.expectGET('some/template2.html'); 222 | 223 | ModalService.showModal({ 224 | controller: "DomController", 225 | templateUrl: "some/template2.html", 226 | locationChangeSuccess : 10 227 | }).then((modal) => { 228 | 229 | $rootScope.$emit('$locationChangeSuccess'); 230 | expect($timeout.verifyNoPendingTasks).to.throw(); 231 | expect(document.getElementById('template2')).not.to.equal(null); 232 | 233 | modal.close.then((result) => { 234 | $timeout.verifyNoPendingTasks(); 235 | expect(document.getElementById('template2')).to.equal(null); 236 | }); 237 | 238 | $rootScope.$emit('$locationChangeSuccess'); 239 | 240 | }); 241 | 242 | $httpBackend.flush(); 243 | $timeout.flush(); 244 | 245 | }); 246 | 247 | }); 248 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import 'angular'; 2 | import 'angular-mocks'; 3 | import '../src/angular-modal-service'; 4 | 5 | // require all modules ending in ".spec" from the 6 | // current directory and all subdirectories 7 | let testsContext = require.context(".", true, /.spec.js$/); 8 | testsContext.keys().forEach(testsContext); 9 | -------------------------------------------------------------------------------- /test/parameters.spec.js: -------------------------------------------------------------------------------- 1 | describe('parameters', () => { 2 | 3 | let ModalService = null; 4 | let rootScope = null; 5 | 6 | 7 | angular.module('parametertests', ['angularModalService']) 8 | .controller('ValidController', ($scope, close) => { 9 | $scope.close = close; 10 | }); 11 | 12 | beforeEach(() => { 13 | angular.mock.module('parametertests'); 14 | inject((_ModalService_, $rootScope) => { 15 | ModalService = _ModalService_; 16 | rootScope = $rootScope; 17 | }); 18 | }); 19 | 20 | it('should fail if there is no controller specified', function(done) { 21 | 22 | ModalService.showModal({ 23 | templateUrl: "some/template.html" 24 | // note, no controller is specified, so we should fail. 25 | }).then(function(modal) { 26 | // We should never get here! 27 | expect(true).to.equal(false); 28 | done(); 29 | }).catch(function(error) { 30 | expect(error).to.equal("No controller has been specified."); 31 | done(); 32 | }); 33 | 34 | rootScope.$apply(); 35 | 36 | }); 37 | 38 | it('should fail if there is no template or template url specified', function(done) { 39 | 40 | ModalService.showModal({ 41 | controller: "SomeController" 42 | // note, no template or template url is specified, so we should fail. 43 | }).then(function(modal) { 44 | // We should never get here! 45 | expect(true).to.equal(false); 46 | done(); 47 | }).catch(function(error) { 48 | expect(error).to.equal("No template or templateUrl has been specified."); 49 | done(); 50 | }); 51 | 52 | rootScope.$apply(); 53 | 54 | }); 55 | 56 | it('should accept the template provided as a string', function(done) { 57 | 58 | ModalService.showModal({ 59 | controller: "ValidController", 60 | template: "
A template
" 61 | }).then(function(modal) { 62 | expect(modal.element.html()).to.equal("A template"); 63 | done(); 64 | }); 65 | 66 | 67 | rootScope.$apply(); 68 | 69 | }); 70 | 71 | it('should accept the controller provided as a function', function(done) { 72 | 73 | ModalService.showModal({ 74 | controller: function($scope){ 75 | $scope.test = "here"; 76 | }, 77 | template: "
A template
" 78 | }).then(function(modal) { 79 | expect(modal.element.html()).to.equal("A template"); 80 | done(); 81 | }); 82 | 83 | 84 | rootScope.$apply(); 85 | 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/template.spec.js: -------------------------------------------------------------------------------- 1 | var sinon = require('sinon'); 2 | 3 | describe('template', () => { 4 | 5 | var ModalService = null; 6 | var rootScope = null; 7 | var $httpBackend = null; 8 | 9 | angular.module('templatetests', ['angularModalService']) 10 | .controller('TemplateController', ($scope) => { 11 | }); 12 | 13 | beforeEach(() => { 14 | angular.mock.module('templatetests'); 15 | inject((_ModalService_, $rootScope, $injector) => { 16 | ModalService = _ModalService_; 17 | rootScope = $rootScope; 18 | $httpBackend = $injector.get('$httpBackend'); 19 | $httpBackend.when('GET', 'some/template.html').respond("
template
"); 20 | $httpBackend.when('GET', 'some/invalid/template.html').respond(404, 'Not Found'); 21 | }); 22 | }); 23 | 24 | afterEach(inject(function($templateCache) { 25 | $httpBackend.verifyNoOutstandingExpectation(); 26 | $httpBackend.verifyNoOutstandingRequest(); 27 | $templateCache.removeAll(); 28 | sinon.restore(); 29 | })); 30 | 31 | it('should http get the specified template url', function() { 32 | 33 | $httpBackend.expectGET('some/template.html'); 34 | 35 | ModalService.showModal({ 36 | controller: "TemplateController", 37 | templateUrl: "some/template.html" 38 | }).then(function(modal) { 39 | expect(modal).not.to.equal(null); 40 | }); 41 | 42 | $httpBackend.flush(); 43 | 44 | }); 45 | 46 | it('should fail to get an invalid template url', function() { 47 | 48 | ModalService.showModal({ 49 | controller: "TemplateController", 50 | templateUrl: "some/invalid/template.html" 51 | }).then(function(modal) { 52 | 53 | }).catch(function(error) { 54 | expect(error).not.to.equal(null); 55 | }); 56 | 57 | $httpBackend.flush(); 58 | 59 | }); 60 | 61 | it('should use the template cache for subsequent requests for the same template', 62 | inject(function($templateCache) { 63 | 64 | $httpBackend.expectGET('templatetobecached.html').respond('
template
'); 65 | 66 | ModalService.showModal({ 67 | controller: "TemplateController", 68 | templateUrl: "templatetobecached.html" 69 | }).then(function(modal) { 70 | 71 | // The template should now be cached... 72 | sinon.spy($templateCache, 'get'); 73 | 74 | ModalService.showModal({ 75 | controller: "TemplateController", 76 | templateUrl: "templatetobecached.html" 77 | }).then(function(modal) { 78 | // ...so get should have been called. 79 | expect(modal).not.to.equal(null); 80 | expect($templateCache.get.called).to.equal(true); 81 | expect($templateCache.get.getCall(0).args[0]).to.equal('templatetobecached.html'); 82 | }); 83 | 84 | }); 85 | 86 | $httpBackend.flush(); 87 | 88 | })); 89 | 90 | it('should use the template cache correctly if the template is precached', 91 | inject(function($templateCache, $http) { 92 | 93 | $httpBackend.expectGET('templatetobeprecached.html').respond('
template
'); 94 | 95 | // Fetch the template (i.e. precache). 96 | $http.get('templatetobeprecached.html', {cache: $templateCache}); 97 | 98 | // The template should now be cached... 99 | sinon.spy($templateCache, 'get'); 100 | 101 | ModalService.showModal({ 102 | controller: "TemplateController", 103 | templateUrl: "templatetobeprecached.html" 104 | }).then(function(modal) { 105 | // ...so get should have been called. 106 | expect($templateCache.get.called).to.equal(true); 107 | expect($templateCache.get.getCall(0).args[0]).to.equal('templatetobeprecached.html'); 108 | }); 109 | 110 | $httpBackend.flush(); 111 | 112 | })); 113 | 114 | }); 115 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | 5 | // Defines the entrypoint of our application. 6 | entry: path.resolve(__dirname, 'src/angular-modal-service.js'), 7 | 8 | // Bundle to ./dst. 9 | output: { 10 | path: path.resolve(__dirname, 'dst'), 11 | filename: 'angular-modal-service.js' 12 | }, 13 | 14 | // Make sure we include sourcemaps. This is for the bundled 15 | // code, not the uglfied code (we uglify with npm run build, 16 | // see package.json for details). 17 | devtool: 'inline-source-map', 18 | 19 | // Define externals (things we don't pack). 20 | externals: { 21 | angular: 'angular', 22 | }, 23 | 24 | // All JavaScript code goes through the babel loader. 25 | module: { 26 | rules: [ 27 | { 28 | test: /\.m?js$/, 29 | exclude: /(node_modules|bower_components)/, 30 | use: { 31 | loader: 'babel-loader', 32 | options: { 33 | presets: ['@babel/preset-env'] 34 | } 35 | } 36 | } 37 | ] 38 | } 39 | }; 40 | --------------------------------------------------------------------------------