├── .bowerrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── bower.json ├── build.config.js ├── dist ├── angular-fancy-modal-theme-classic.css ├── angular-fancy-modal-theme-classic.min.css ├── angular-fancy-modal.css ├── angular-fancy-modal.js ├── angular-fancy-modal.min.css └── angular-fancy-modal.min.js ├── example ├── app.js ├── apple-touch-icon-precomposed.png ├── controller-as.html ├── favicon.ico ├── index.html ├── loading.html ├── long.html ├── main.css └── resolve-template.html ├── gulpfile.js ├── index.html ├── karma.config.js ├── lib ├── angular-fancy-modal-theme-classic.scss ├── angular-fancy-modal.js └── angular-fancy-modal.scss ├── package.json └── test └── angular-fancy-modal.spec.js /.bowerrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesparny/angular-fancy-modal/7947e57be9388e668b7edab09d4c0205948cb052/.bowerrc -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = spaces 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### OSX ### 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear on external disk 16 | .Spotlight-V100 17 | .Trashes 18 | 19 | # Directories potentially created on remote AFP share 20 | .AppleDB 21 | .AppleDesktop 22 | Network Trash Folder 23 | Temporary Items 24 | .apdisk 25 | 26 | 27 | ### Windows ### 28 | # Windows image file caches 29 | Thumbs.db 30 | ehthumbs.db 31 | 32 | # Folder config file 33 | Desktop.ini 34 | 35 | # Recycle Bin used on file shares 36 | $RECYCLE.BIN/ 37 | 38 | # Windows Installer files 39 | *.cab 40 | *.msi 41 | *.msm 42 | *.msp 43 | 44 | # Windows shortcuts 45 | *.lnk 46 | 47 | 48 | ### Node ### 49 | # Logs 50 | logs 51 | *.log 52 | 53 | # Runtime data 54 | pids 55 | *.pid 56 | *.seed 57 | 58 | # Directory for instrumented libs generated by jscoverage/JSCover 59 | lib-cov 60 | 61 | # Coverage directory used by tools like istanbul 62 | coverage 63 | 64 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 65 | .grunt 66 | 67 | 68 | # Dependency directory 69 | # Commenting this out is preferred by some people, see 70 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 71 | node_modules 72 | bower_components 73 | npm-debug.log 74 | 75 | # Users Environment Variables 76 | .lock-wscript 77 | 78 | 79 | ### Eclipse ### 80 | *.pydevproject 81 | .metadata 82 | .gradle 83 | bin/ 84 | tmp/ 85 | *.tmp 86 | *.bak 87 | *.swp 88 | *~.nib 89 | local.properties 90 | .settings/ 91 | .loadpath 92 | 93 | # External tool builders 94 | .externalToolBuilders/ 95 | 96 | # Locally stored "Eclipse launch configurations" 97 | *.launch 98 | 99 | # CDT-specific 100 | .cproject 101 | 102 | # PDT-specific 103 | .buildpath 104 | 105 | # sbteclipse plugin 106 | .target 107 | 108 | # TeXlipse plugin 109 | .texlipse 110 | 111 | 112 | ### Intellij ### 113 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 114 | 115 | /*.iml 116 | 117 | ## Directory-based project format: 118 | .idea/ 119 | 120 | # mpeltonen/sbt-idea plugin 121 | .idea_modules/ 122 | 123 | ### SublimeText ### 124 | # cache files for sublime text 125 | *.tmlanguage.cache 126 | *.tmPreferences.cache 127 | *.stTheme.cache 128 | 129 | # workspace files are user-specific 130 | *.sublime-workspace 131 | 132 | # project files should be checked into the repository, unless a significant 133 | # proportion of contributors will probably not be using SublimeText 134 | # *.sublime-project 135 | 136 | # sftp configuration file 137 | sftp-config.json 138 | 139 | 140 | ### Sass ### 141 | .sass-cache 142 | *.css.map 143 | 144 | 145 | ### Linux ### 146 | *~ 147 | 148 | # KDE directory preferences 149 | .directory 150 | 151 | # Grunt usually preprocesses files such as coffeescript, compass... inside the .tmp directory 152 | .tmp/ 153 | 154 | ### project ### 155 | 156 | unit-results 157 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "angular": false 4 | }, 5 | "node": true, 6 | "browser": true, 7 | "bitwise": true, 8 | "camelcase": true, 9 | "curly": true, 10 | "immed": true, 11 | "latedef": true, 12 | "newcap": true, 13 | "trailing": true, 14 | "quotmark": "single", 15 | "strict": true, 16 | "multistr": true, 17 | "debug": false, 18 | "forin": true, 19 | "undef": true, 20 | "plusplus": true, 21 | "eqeqeq": true, 22 | "validthis": false, 23 | "unused": true 24 | 25 | } 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | before_install: 5 | - 'gem update --system' 6 | - 'gem install sass' 7 | - 'npm install -g gulp' 8 | - 'npm install -g bower' 9 | - 'bower install' 10 | script: 11 | - 'gulp travis' 12 | branches: 13 | only: 14 | - master 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.1.4 (2015-05-20) 2 | 3 | * fixing typos 4 | 5 | ### 0.1.3 (2015-05-20) 6 | 7 | * fixing parent scope destroy 8 | 9 | ### 0.1.2 (2014-11-19) 10 | 11 | * fixing dist version 12 | 13 | ### 0.1.1 (2014-11-19) 14 | 15 | * bower release 16 | 17 | ### 0.1.0 (2014-11-19) 18 | 19 | * first release 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Pull requests 2 | 3 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 4 | and configure the remotes: 5 | 6 | ```bash 7 | # Clone your fork of the repo into the current directory 8 | git clone https://github.com//angular-fancy-modal.git 9 | # Navigate to the newly cloned directory 10 | cd angular-fancy-modal 11 | # Assign the original repo to a remote called "upstream" 12 | git remote add upstream https://github.com/vesparny/angular-fancy-modal.git 13 | ``` 14 | 15 | 2. If you cloned a while ago, get the latest changes from upstream: 16 | 17 | ```bash 18 | git checkout master 19 | git pull upstream master 20 | ``` 21 | 22 | 3. Create a new topic branch (off the main project development branch) to 23 | contain your feature, change, or fix: 24 | 25 | ```bash 26 | git checkout -b 27 | ``` 28 | 29 | 4. Commit your changes in logical chunks. Use Git's 30 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 31 | feature to tidy up your commits before making them public. 32 | 33 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 34 | 35 | ```bash 36 | git pull [--rebase] upstream master 37 | ``` 38 | 39 | 6. Push your topic branch up to your fork: 40 | 41 | ```bash 42 | git push origin 43 | ``` 44 | 45 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 46 | with a clear title and description. 47 | 48 | **IMPORTANT**: By submitting a patch, you agree to allow the project owners to 49 | license your work under the the terms of the [MIT License](LICENSE.md). 50 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) Alessandro Arnodo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # STATUS: UNMAINTAINED 2 | --- 3 | 4 | # [angular-fancy-modal](http://vesparny.github.io/angular-fancy-modal) 5 | 6 | **The definitive modal/popup/dialog solution for AngularJS.** 7 | Modal dialogs and popups for [Angular.js](http://angularjs.org/) (>=1.2.x) applications. 8 | Mobile friendly out of the box. 9 | 10 | [![Build Status](https://secure.travis-ci.org/vesparny/angular-fancy-modal.svg)](http://travis-ci.org/vesparny/angular-fancy-modal) 11 | 12 | **Brought to you by [Alessandro Arnodo](http://alessandro.arnodo.net) [[@vesparny](https://twitter.com/vesparny)]** 13 | 14 | #### See a [working demo with examples on usage](http://vesparny.github.io/angular-fancy-modal/). 15 | 16 | ## Install 17 | 18 | via bower: 19 | 20 | ``` 21 | bower install angular-fancy-modal --save 22 | ``` 23 | 24 | or clone this repository for using the unstable version. 25 | 26 | ## Usage 27 | 28 | You need only to include `angular-fancy-modal.js` and `angular-fancy-modal.css` to your project and then you can start using it: 29 | 30 | ```javascript 31 | var app = angular.module('exampleApp', ['vesparny.fancyModal']); 32 | 33 | app.controller('MyCtrl', function ($scope, $fancyModal) { 34 | $scope.open = function () { 35 | $fancyModal.open({ templateUrl: 'popupTmpl.html' }); 36 | }; 37 | }); 38 | ``` 39 | 40 | ## API 41 | 42 | `$fancyModal` service provides easy to use and minimalistic API, but in the same time it's powerful enough. Here is the list of accessible methods that you can use: 43 | 44 | === 45 | 46 | ### `.open(options)` 47 | 48 | Method allows to open modal dialog. It accepts `options` object as the only argument. 49 | 50 | ### Options: 51 | 52 | `$modal` is a service to quickly create AngularJS-powered modal windows. Creating custom modals is straightforward: create a partial view, its controller and reference them when using the service. 53 | 54 | The `$modal` service has only one method: open(options) where available options are like follows: 55 | 56 | * `templateUrl` - a path to a template representing modal's content 57 | * `template` - inline template representing the modal's content 58 | * `scope` - a scope instance to be used for the modal's content (actually the $modal service is going to create a child scope of a provided scope). Defaults to $rootScope 59 | * `controller` - a controller for a modal instance - it can initialize scope used by modal. Accepts the "controller-as" syntax in the form 'SomeCtrl as myctrl';Every controller will have a `$modal` object instance in its scope, useful for closing the dialog. 60 | * `resolve` - members that will be resolved and passed to the controller as locals; it is equivalent of the resolve property for AngularJS routes 61 | * `showCloseButton` - dispay or not the close button (default true) 62 | * `closeOnEscape` - close dialog by pressing escape key (default true) 63 | * `closeOnOverlayClick` - close dialog by clicking on the overlay (default true) 64 | * `overlay` - display overlay (default true) 65 | * `themeClass` - theme css class name to add to modal window 66 | * `bodyClass` - css class to add to body 67 | * `openingClass` - css class to add to modal content while opening 68 | * `closingClass` - css class to add to modal content while closing 69 | * `openingOverlayClass` - css class to add to modal overlay while opening 70 | * `closingOverlayClass` - css class to add to modal overlay while closing 71 | 72 | === 73 | 74 | ### `.close()` 75 | 76 | Calling the `.close` method on an instance returned by the `.open` method will close the relative modal, calling the method on the `$fancyModal` service will close every opened modal. 77 | 78 | === 79 | 80 | ### `.setDefaults(options)` 81 | 82 | You're able to set default settings through `$fancyModalProvider`: 83 | 84 | ```javascript 85 | var app = angular.module('myApp', ['vesparny.fancyModal']); 86 | app.config(['$fancyModalProvider', function ($fancyModalProvider) { 87 | $fancyModalProvider.setDefaults({ 88 | themeClass: 'fancy-modal-theme-default' 89 | }); 90 | }]); 91 | ``` 92 | 93 | ### Returns: 94 | 95 | The `open()` method returns an object with some useful properties. 96 | 97 | ##### ``id {String}`` 98 | 99 | This is the DOM ID of the modal which was just created. 100 | 101 | ##### `close {Function}` 102 | 103 | This is a function which will close the modal opened before. 104 | 105 | ##### `opened {Promise}` 106 | 107 | A promise which will resolve when the modal is fully loaded. 108 | 109 | Example: 110 | 111 | ```javascript 112 | var modal = $fancyModal.open({ 113 | templateUrl: 'template.html', 114 | }); 115 | modal.opened.then(function() { 116 | modal.close(); 117 | }); 118 | ``` 119 | === 120 | 121 | ## Directive 122 | 123 | By default $fancyModal module is served with `fancy-modal` directive which can be used as attribute for buttons, links, etc. All `.open()` options are available through attributes. 124 | 125 | Example: 126 | 127 | ```html 128 | 132 | ``` 133 | ## Events 134 | 135 | Everytime when $fancyModal is opened or closed we're broadcasting three events (dispatching events downwards to all child scopes): 136 | 137 | - `$fancyModal.opened` 138 | 139 | - `$fancyModal.closed` 140 | 141 | Example: 142 | 143 | ```javascript 144 | $rootScope.$on('$fancyModal.opened', function (e, $modal) { 145 | console.log('$fancyModal opened: ' + $modal.attr('id')); 146 | }); 147 | 148 | $rootScope.$on('$fancyModal.closed', function (e, id) { 149 | console.log('$fancyModal closed: ' + id); 150 | }); 151 | ``` 152 | 153 | ## Themes 154 | 155 | The project contains one theme that show how easily you can create your own. Check `example` folder for demonstration purposes. 156 | 157 | ## References 158 | 159 | Inspired by awesome [Hubspot/Vex](https://github.com/HubSpot/vex) jQuery modals and [ngDialog](https://github.com/likeastore/ngDialog) 160 | 161 | ## Test 162 | 163 | clone this repository then 164 | 165 | ``` 166 | npm install 167 | npm install -g gulp 168 | bower install 169 | gulp test 170 | ``` 171 | 172 | ### Contributing 173 | 174 | PR and issues reporting are always welcome :) 175 | see also CONTRIBUTING.md file. 176 | 177 | ### License 178 | 179 | See LICENSE.md file 180 | 181 | ### Changelog 182 | 183 | See CHANGELOG.md file 184 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-fancy-modal", 3 | "authors": [ 4 | "Alessandro Arnodo (http://alessandro.arnodo.net)" 5 | ], 6 | "version": "0.1.4", 7 | "homepage": "https://github.com/vesparny/angular-fancy-modal", 8 | "description": "the definitive modal/popup/dialog solution for AngularJS.", 9 | "main": [ 10 | "dist/angular-fancy-modal.js", 11 | "dist/angular-fancy-modal.css", 12 | "dist/angular-fancy-modal-theme-default.css" 13 | ], 14 | "keywords": [ 15 | "angularjs", 16 | "modal", 17 | "popup", 18 | "dialog", 19 | "ng", 20 | "provider", 21 | "factory", 22 | "directive", 23 | "service" 24 | ], 25 | "license": "MIT", 26 | "ignore": [ 27 | "**/.*", 28 | "node_modules", 29 | "bower_components", 30 | "test", 31 | "lib", 32 | "example", 33 | "gulpfile.js", 34 | "*.config.js", 35 | "index.html" 36 | ], 37 | "dependencies": { 38 | "angular": ">= 1.2.0" 39 | }, 40 | "devDependencies": { 41 | "angular-mocks": ">= 1.2.0", 42 | "jquery": "~2.1.1" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /build.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | port: 3000, 5 | dist: 'dist', 6 | scss: 'lib/*.scss', 7 | js: 'lib/*.js', 8 | banner: ['/**', 9 | ' * <%= pkg.name %> - <%= pkg.description %>', 10 | ' * @author <%= pkg.author %>', 11 | ' * @url <%= pkg.url %>', 12 | ' * @version v<%= pkg.version %>', 13 | ' * @link <%= pkg.homepage %>', 14 | ' * @license <%= pkg.license %>', 15 | ' */', 16 | '' 17 | ].join('\n') 18 | }; 19 | -------------------------------------------------------------------------------- /dist/angular-fancy-modal-theme-classic.css: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-fancy-modal - the definitive modal/popup/dialog solution for AngularJS. 3 | * @author Alessandro Arnodo 4 | * @url http://alessandro.arnodo.net 5 | * @version v0.1.4 6 | * @link https://github.com/vesparny/angular-fancy-modal 7 | * @license MIT 8 | */ 9 | .fancymodal.fancymodal-theme-classic .fancymodal-content { 10 | max-width: 420px; 11 | background-color: #000; 12 | color: #fff; } 13 | 14 | .fancymodal.fancymodal-theme-classic .fancymodal-content-opening { 15 | -webkit-animation: fancymodal-in 1s; 16 | animation: fancymodal-in 1s; } 17 | 18 | .fancymodal.fancymodal-theme-classic .fancymodal-content-closing { 19 | -webkit-animation: fancymodal-out 1s; 20 | animation: fancymodal-out 1s; } 21 | 22 | .fancymodal.fancymodal-theme-classic .fancymodal-overlay-opening { 23 | -webkit-animation: fancymodal-fadeIn 1s; 24 | animation: fancymodal-fadeIn 1s; } 25 | 26 | .fancymodal.fancymodal-theme-classic .fancymodal-overlay-closing { 27 | -webkit-animation: fancymodal-fadeOut 1s; 28 | animation: fancymodal-fadeOut 1s; } 29 | -------------------------------------------------------------------------------- /dist/angular-fancy-modal-theme-classic.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-fancy-modal - the definitive modal/popup/dialog solution for AngularJS. 3 | * @author Alessandro Arnodo 4 | * @url http://alessandro.arnodo.net 5 | * @version v0.1.4 6 | * @link https://github.com/vesparny/angular-fancy-modal 7 | * @license MIT 8 | */ 9 | .fancymodal.fancymodal-theme-classic .fancymodal-content{max-width:420px;background-color:#000;color:#fff}.fancymodal.fancymodal-theme-classic .fancymodal-content-opening{-webkit-animation:fancymodal-in 1s;animation:fancymodal-in 1s}.fancymodal.fancymodal-theme-classic .fancymodal-content-closing{-webkit-animation:fancymodal-out 1s;animation:fancymodal-out 1s}.fancymodal.fancymodal-theme-classic .fancymodal-overlay-opening{-webkit-animation:fancymodal-fadeIn 1s;animation:fancymodal-fadeIn 1s}.fancymodal.fancymodal-theme-classic .fancymodal-overlay-closing{-webkit-animation:fancymodal-fadeOut 1s;animation:fancymodal-fadeOut 1s} -------------------------------------------------------------------------------- /dist/angular-fancy-modal.css: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-fancy-modal - the definitive modal/popup/dialog solution for AngularJS. 3 | * @author Alessandro Arnodo 4 | * @url http://alessandro.arnodo.net 5 | * @version v0.1.4 6 | * @link https://github.com/vesparny/angular-fancy-modal 7 | * @license MIT 8 | */ 9 | body.fancymodal-open { 10 | overflow: hidden; } 11 | 12 | .fancymodal, 13 | .fancymodal *, 14 | .fancymodal *:before, 15 | .fancymodal *:after { 16 | -webkit-box-sizing: border-box; 17 | -moz-box-sizing: border-box; 18 | box-sizing: border-box; } 19 | 20 | .fancymodal { 21 | padding-top: 120px; 22 | padding-bottom: 160px; 23 | -webkit-overflow-scrolling: touch; 24 | position: fixed; 25 | overflow: auto; 26 | top: 0; 27 | right: 0; 28 | bottom: 0; 29 | left: 0; 30 | z-index: 1050; 31 | padding-left: 8px; 32 | padding-right: 8px; } 33 | .fancymodal .fancymodal-overlay { 34 | -webkit-backface-visibility: hidden; 35 | background: rgba(0, 0, 0, 0.3); 36 | position: fixed; 37 | top: 0; 38 | right: 0; 39 | bottom: 0; 40 | left: 0; } 41 | .fancymodal .fancymodal-content { 42 | max-width: 500px; 43 | background: #fff; 44 | -webkit-backface-visibility: hidden; 45 | -webkit-border-radius: 5px; 46 | -moz-border-radius: 5px; 47 | -ms-border-radius: 5px; 48 | -o-border-radius: 5px; 49 | padding: 2em 1em; 50 | border-radius: 5px; 51 | background: #eee; 52 | color: #000; 53 | position: relative; 54 | margin: 0 auto; } 55 | .fancymodal .fancymodal-close { 56 | border-radius: 3px; 57 | position: absolute; 58 | top: 0; 59 | right: 0; 60 | cursor: pointer; } 61 | .fancymodal .fancymodal-close:before { 62 | border-radius: 3px; 63 | position: absolute; 64 | content: "\00D7"; 65 | font-size: 26px; 66 | line-height: 30px; 67 | height: 30px; 68 | width: 30px; 69 | text-align: center; 70 | top: 3px; 71 | right: 3px; 72 | color: #ccc; } 73 | .fancymodal .fancymodal-close:hover:before, 74 | .fancymodal .fancymodal-close:active:before { 75 | color: #777; 76 | background: #e0e0e0; } 77 | .fancymodal .fancymodal-content-opening { 78 | -webkit-animation: fancymodal-in 0.5s; 79 | animation: fancymodal-in 0.5s; } 80 | .fancymodal .fancymodal-content-closing { 81 | -webkit-animation: fancymodal-out 0.5s; 82 | animation: fancymodal-out 0.5s; } 83 | .fancymodal .fancymodal-overlay-opening { 84 | -webkit-animation: fancymodal-fadeIn 0.5s; 85 | animation: fancymodal-fadeIn 0.5s; } 86 | .fancymodal .fancymodal-overlay-closing { 87 | -webkit-animation: fancymodal-fadeOut 0.5s; 88 | animation: fancymodal-fadeOut 0.5s; } 89 | 90 | @-webkit-keyframes fancymodal-in { 91 | 0% { 92 | opacity: 0; 93 | -webkit-transform: translateY(-40px); 94 | transform: translateY(-40px); } 95 | 100% { 96 | opacity: 1; 97 | -webkit-transform: translateY(0); 98 | transform: translateY(0); } } 99 | 100 | @keyframes fancymodal-in { 101 | 0% { 102 | opacity: 0; 103 | -webkit-transform: translateY(-40px); 104 | transform: translateY(-40px); } 105 | 100% { 106 | opacity: 1; 107 | -webkit-transform: translateY(0); 108 | transform: translateY(0); } } 109 | 110 | @-webkit-keyframes fancymodal-out { 111 | 0% { 112 | opacity: 1; 113 | -webkit-transform: translateY(0); 114 | transform: translateY(0); } 115 | 100% { 116 | opacity: 0; 117 | -webkit-transform: translateY(-40px); 118 | transform: translateY(-40px); } } 119 | 120 | @keyframes fancymodal-out { 121 | 0% { 122 | opacity: 1; 123 | -webkit-transform: translateY(0); 124 | transform: translateY(0); } 125 | 100% { 126 | opacity: 0; 127 | -webkit-transform: translateY(-40px); 128 | transform: translateY(-40px); } } 129 | 130 | @-webkit-keyframes fancymodal-fadeIn { 131 | 0% { 132 | opacity: 0; } 133 | 100% { 134 | opacity: 1; } } 135 | 136 | @keyframes fancymodal-fadeIn { 137 | 0% { 138 | opacity: 0; } 139 | 100% { 140 | opacity: 1; } } 141 | 142 | @-webkit-keyframes fancymodal-fadeOut { 143 | 0% { 144 | opacity: 1; } 145 | 100% { 146 | opacity: 0; } } 147 | 148 | @keyframes fancymodal-fadeOut { 149 | 0% { 150 | opacity: 1; } 151 | 100% { 152 | opacity: 0; } } 153 | -------------------------------------------------------------------------------- /dist/angular-fancy-modal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-fancy-modal - the definitive modal/popup/dialog solution for AngularJS. 3 | * @author Alessandro Arnodo 4 | * @url http://alessandro.arnodo.net 5 | * @version v0.1.4 6 | * @link https://github.com/vesparny/angular-fancy-modal 7 | * @license MIT 8 | */ 9 | (function(angular, document, window, undefined) { 10 | 'use strict'; 11 | angular.module('vesparny.fancyModal', []). 12 | provider('$fancyModal', function() { 13 | var defaults = this.defaults = { 14 | templateUrl: '', 15 | template: '
', 16 | resolve: {}, 17 | controller: '', 18 | scope: '', 19 | showCloseButton: true, 20 | closeOnEscape: true, 21 | closeOnOverlayClick: true, 22 | overlay: true, 23 | themeClass: '', 24 | openingClass: 'fancymodal-content-opening', 25 | closingClass: 'fancymodal-content-closing', 26 | openingOverlayClass: 'fancymodal-overlay-opening', 27 | closingOverlayClass: 'fancymodal-overlay-closing', 28 | bodyClass: 'fancymodal-open' 29 | }; 30 | 31 | /** 32 | * [setDefaults overriding defaults] 33 | * @param {[Object]} options [new defaults] 34 | */ 35 | this.setDefaults = function(options) { 36 | angular.extend(this.defaults, options); 37 | }; 38 | 39 | this.$get = ['$controller', '$timeout', '$rootScope', '$injector', '$compile', '$http', '$templateCache', '$window', '$document', '$q', 40 | function($controller, $timeout, $rootScope, $injector, $compile, $http, $templateCache, $window, $document, $q) { 41 | var modalCounter = 0; 42 | var incrementalId = 0; 43 | var isClosing = false; 44 | var style = (document.body || document.documentElement).style; 45 | var animationEndSupport = angular.isDefined(style.animation) || angular.isDefined(style.WebkitAnimation) || angular.isDefined(style.MozAnimation) || angular.isDefined(style.MsAnimation) || angular.isDefined(style.OAnimation); 46 | var animationEndEvent = 'animationend webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend'; 47 | var $body = angular.element(document).find('body'); 48 | var $html = angular.element(document).find('html'); 49 | 50 | // private methds 51 | 52 | function onKeydown(event) { 53 | if (event.keyCode === 27) { 54 | close(); 55 | } 56 | } 57 | 58 | //http://davidwalsh.name/detect-scrollbar-width 59 | function getScrollBarWidth() { 60 | var scrollDiv = document.createElement('div'); 61 | scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;'; 62 | document.body.appendChild(scrollDiv); 63 | var size = scrollDiv.offsetWidth - scrollDiv.clientWidth; 64 | document.body.removeChild(scrollDiv); 65 | return size; 66 | } 67 | 68 | function hasScrollbars() { 69 | return $body[0].scrollHeight > $window.innerHeight; 70 | } 71 | 72 | function getTemplatePromise(options) { 73 | var deferred = $q.defer(); 74 | if (options.templateUrl) { 75 | $http.get(options.templateUrl, { 76 | cache: $templateCache 77 | }).then(function(result) { 78 | deferred.resolve(result.data); 79 | }); 80 | } else { 81 | deferred.resolve(options.template); 82 | } 83 | return deferred.promise; 84 | } 85 | 86 | function getResolvePromises(resolves) { 87 | var promisesArr = []; 88 | angular.forEach(resolves, function(value) { 89 | promisesArr.push($q.when($injector.invoke(value))); 90 | }); 91 | return promisesArr; 92 | } 93 | 94 | function cleanUp($modal) { 95 | var elementId = $modal.attr('id'); 96 | $modal.scope().$destroy(); 97 | $modal.remove(); 98 | isClosing = false; 99 | if (modalCounter === 0) { 100 | $body.removeClass('fancymodal-open'); 101 | $html.css('margin-right', ''); 102 | } 103 | $rootScope.$emit('$fancyModal.closed', elementId); 104 | } 105 | 106 | function closeModal(modal) { 107 | var $modal = angular.element(modal); 108 | var options = $modal.data('options'); 109 | isClosing = true; 110 | $modal.unbind('click'); 111 | 112 | if (modalCounter === 1) { 113 | $document.unbind('keydown'); 114 | } 115 | 116 | modalCounter -= 1; 117 | 118 | if (animationEndSupport) { 119 | var content = angular.element(modal.children[0]); 120 | if (options.overlay) { 121 | var overlay = angular.element(modal.children[0]); 122 | overlay.addClass(options.closingOverlayClass); 123 | content = angular.element(modal.children[1]); 124 | } 125 | content.unbind(animationEndEvent).bind(animationEndEvent, function() { 126 | content.remove(); 127 | cleanUp($modal); 128 | }).removeClass(options.openingClass).addClass(options.closingClass); 129 | } else { 130 | cleanUp($modal); 131 | } 132 | } 133 | 134 | function closeAll() { 135 | var modals = document.querySelectorAll('.fancymodal'); 136 | angular.forEach(modals, function(modal) { 137 | closeModal(modal); 138 | }); 139 | } 140 | 141 | // public API 142 | 143 | /** 144 | * open a modal 145 | * @param {[object]} opts [modal params] 146 | * @return Promise 147 | */ 148 | function open(opts) { 149 | 150 | function closeByAction(event) { 151 | var overlay = angular.element(event.target).hasClass('fancymodal-overlay'); 152 | var closeBtn = angular.element(event.target).hasClass('fancymodal-close'); 153 | 154 | if ((overlay && options.closeOnOverlayClick) || closeBtn) { 155 | close(modal.attr('id')); 156 | } 157 | } 158 | 159 | function execOpen($modal) { 160 | var defer = $q.defer(); 161 | var htmlTemplate; 162 | getTemplatePromise(options).then(function(template) { 163 | htmlTemplate = template; 164 | return $q.all(getResolvePromises(options.resolve)); 165 | }).then(function(locals) { 166 | if (!isClosing) { 167 | var data = {}; 168 | var resolveCounter = 0; 169 | var ctrl; 170 | angular.forEach(options.resolve, function(value, key) { 171 | data[key] = locals[resolveCounter]; 172 | resolveCounter += 1; 173 | }); 174 | scope.$modal = $modal; 175 | data.$scope = scope; 176 | if (options.controller) { 177 | ctrl = $controller(options.controller, data); 178 | } 179 | contentData.append($compile(htmlTemplate)(scope)); 180 | $rootScope.$emit('$fancyModal.opened', modal); 181 | defer.resolve(); 182 | } else { 183 | defer.reject(); 184 | } 185 | }); 186 | return defer.promise; 187 | } 188 | 189 | var options = angular.copy(defaults); 190 | opts = opts || {}; 191 | angular.extend(options, opts); 192 | modalCounter += 1; 193 | incrementalId += 1; 194 | 195 | $body.addClass(options.bodyClass); 196 | 197 | if (hasScrollbars()) { 198 | $html.css('margin-right', getScrollBarWidth() + 'px'); 199 | } 200 | 201 | var scope = (options.scope || $rootScope).$new(); 202 | var modal = angular.element('
').addClass(options.themeClass); 203 | modal.data('options', options); 204 | var content = angular.element('
').addClass('fancymodal-content'); 205 | 206 | if (options.showCloseButton) { 207 | var closeButton = angular.element('
').addClass('fancymodal-close'); 208 | content.append(closeButton); 209 | } 210 | 211 | if (options.closeOnEscape) { 212 | $document.bind('keydown', onKeydown); 213 | } 214 | 215 | var contentData = angular.element('
').addClass('fancymodal-data'); 216 | 217 | if (options.overlay) { 218 | var overlay = angular.element('
').addClass('fancymodal-overlay').addClass(options.openingOverlayClass); 219 | modal.append(overlay); 220 | } 221 | 222 | content.append(contentData); 223 | content.addClass(options.openingClass); 224 | 225 | modal.bind('click', closeByAction); 226 | modal.append(content); 227 | $body.append($compile(modal)(scope)); 228 | var id = 'fancymodal-'+incrementalId; 229 | var $modal = { 230 | id: id, 231 | close: function() { 232 | return close(id); 233 | } 234 | }; 235 | // open the dialog 236 | var openPromise = execOpen($modal); 237 | 238 | return angular.extend({ 239 | opened: openPromise 240 | }, $modal); 241 | } 242 | /** 243 | * [close close a modal] 244 | * @param {[integer]} id [the id of the modal to be closed] 245 | */ 246 | function close(id) { 247 | var modal = document.getElementById(id); 248 | if (modal) { 249 | closeModal(modal); 250 | } else { 251 | closeAll(); 252 | } 253 | } 254 | 255 | return { 256 | open: function(options) { 257 | return open(options); 258 | }, 259 | close: function(id) { 260 | return close(id); 261 | }, 262 | getDefaults: function() { 263 | return angular.copy(defaults); 264 | } 265 | }; 266 | } 267 | ]; 268 | }).directive('fancyModal', ['$fancyModal', function($fancyModal) { 269 | return { 270 | restrict: 'A', 271 | link: function(scope, element, attrs) { 272 | var modal; 273 | var options; 274 | // Trigger 275 | element.on(attrs.trigger || 'click', function() { 276 | options = $fancyModal.getDefaults(); 277 | angular.forEach(Object.keys(options), function(key) { 278 | if (angular.isDefined(attrs[key])) { 279 | //TODO fix this, it sucks bad 280 | if (attrs[key] === 'true') { 281 | options[key] = true; 282 | } else if (attrs[key] === 'false') { 283 | options[key] = false; 284 | } else { 285 | options[key] = attrs[key]; 286 | } 287 | } 288 | }); 289 | modal = $fancyModal.open(options); 290 | }); 291 | 292 | // Garbage collection 293 | scope.$on('$destroy', function() { 294 | if (modal) { 295 | modal.opened.then(function() { 296 | modal.close(); 297 | modal = null; 298 | }); 299 | } else { 300 | modal = null; 301 | } 302 | options = null; 303 | }); 304 | } 305 | }; 306 | }]); 307 | })(angular, document, window); 308 | -------------------------------------------------------------------------------- /dist/angular-fancy-modal.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-fancy-modal - the definitive modal/popup/dialog solution for AngularJS. 3 | * @author Alessandro Arnodo 4 | * @url http://alessandro.arnodo.net 5 | * @version v0.1.4 6 | * @link https://github.com/vesparny/angular-fancy-modal 7 | * @license MIT 8 | */ 9 | body.fancymodal-open{overflow:hidden}.fancymodal,.fancymodal *,.fancymodal *:before,.fancymodal *:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fancymodal{-webkit-overflow-scrolling:touch;position:fixed;overflow:auto;top:0;right:0;bottom:0;left:0;z-index:1050;padding:120px 8px 160px}.fancymodal .fancymodal-overlay{-webkit-backface-visibility:hidden;background:rgba(0,0,0,.3);position:fixed;top:0;right:0;bottom:0;left:0}.fancymodal .fancymodal-content{max-width:500px;background:#fff;-webkit-backface-visibility:hidden;-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;padding:2em 1em;border-radius:5px;background:#eee;color:#000;position:relative;margin:0 auto}.fancymodal .fancymodal-close{border-radius:3px;position:absolute;top:0;right:0;cursor:pointer}.fancymodal .fancymodal-close:before{border-radius:3px;position:absolute;content:"\00D7";font-size:26px;line-height:30px;height:30px;width:30px;text-align:center;top:3px;right:3px;color:#ccc}.fancymodal .fancymodal-close:hover:before,.fancymodal .fancymodal-close:active:before{color:#777;background:#e0e0e0}.fancymodal .fancymodal-content-opening{-webkit-animation:fancymodal-in .5s;animation:fancymodal-in .5s}.fancymodal .fancymodal-content-closing{-webkit-animation:fancymodal-out .5s;animation:fancymodal-out .5s}.fancymodal .fancymodal-overlay-opening{-webkit-animation:fancymodal-fadeIn .5s;animation:fancymodal-fadeIn .5s}.fancymodal .fancymodal-overlay-closing{-webkit-animation:fancymodal-fadeOut .5s;animation:fancymodal-fadeOut .5s}@-webkit-keyframes fancymodal-in{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fancymodal-in{0%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes fancymodal-out{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes fancymodal-out{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);transform:translateY(-40px)}}@-webkit-keyframes fancymodal-fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fancymodal-fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes fancymodal-fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fancymodal-fadeOut{0%{opacity:1}100%{opacity:0}} -------------------------------------------------------------------------------- /dist/angular-fancy-modal.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-fancy-modal - the definitive modal/popup/dialog solution for AngularJS. 3 | * @author Alessandro Arnodo 4 | * @url http://alessandro.arnodo.net 5 | * @version v0.1.4 6 | * @link https://github.com/vesparny/angular-fancy-modal 7 | * @license MIT 8 | */ 9 | !function(e,n,o,a){"use strict";e.module("vesparny.fancyModal",[]).provider("$fancyModal",function(){var o=this.defaults={templateUrl:"",template:"
",resolve:{},controller:"",scope:"",showCloseButton:!0,closeOnEscape:!0,closeOnOverlayClick:!0,overlay:!0,themeClass:"",openingClass:"fancymodal-content-opening",closingClass:"fancymodal-content-closing",openingOverlayClass:"fancymodal-overlay-opening",closingOverlayClass:"fancymodal-overlay-closing",bodyClass:"fancymodal-open"};this.setDefaults=function(n){e.extend(this.defaults,n)},this.$get=["$controller","$timeout","$rootScope","$injector","$compile","$http","$templateCache","$window","$document","$q",function(a,t,l,i,s,r,d,c,f,m){function u(e){27===e.keyCode&&k()}function v(){var e=n.createElement("div");e.style.cssText="width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;",n.body.appendChild(e);var o=e.offsetWidth-e.clientWidth;return n.body.removeChild(e),o}function p(){return x[0].scrollHeight>c.innerHeight}function y(e){var n=m.defer();return e.templateUrl?r.get(e.templateUrl,{cache:d}).then(function(e){n.resolve(e.data)}):n.resolve(e.template),n.promise}function h(n){var o=[];return e.forEach(n,function(e){o.push(m.when(i.invoke(e)))}),o}function C(e){var n=e.attr("id");e.scope().$destroy(),e.remove(),O=!1,0===w&&(x.removeClass("fancymodal-open"),j.css("margin-right","")),l.$emit("$fancyModal.closed",n)}function g(n){var o=e.element(n),a=o.data("options");if(O=!0,o.unbind("click"),1===w&&f.unbind("keydown"),w-=1,D){var t=e.element(n.children[0]);if(a.overlay){var l=e.element(n.children[0]);l.addClass(a.closingOverlayClass),t=e.element(n.children[1])}t.unbind(M).bind(M,function(){t.remove(),C(o)}).removeClass(a.openingClass).addClass(a.closingClass)}else C(o)}function $(){var o=n.querySelectorAll(".fancymodal");e.forEach(o,function(e){g(e)})}function b(n){function t(n){var o=e.element(n.target).hasClass("fancymodal-overlay"),a=e.element(n.target).hasClass("fancymodal-close");(o&&r.closeOnOverlayClick||a)&&k(c.attr("id"))}function i(n){var o,t=m.defer();return y(r).then(function(e){return o=e,m.all(h(r.resolve))}).then(function(i){if(O)t.reject();else{var f,m={},u=0;e.forEach(r.resolve,function(e,n){m[n]=i[u],u+=1}),d.$modal=n,m.$scope=d,r.controller&&(f=a(r.controller,m)),$.append(s(o)(d)),l.$emit("$fancyModal.opened",c),t.resolve()}}),t.promise}var r=e.copy(o);n=n||{},e.extend(r,n),w+=1,E+=1,x.addClass(r.bodyClass),p()&&j.css("margin-right",v()+"px");var d=(r.scope||l).$new(),c=e.element('
').addClass(r.themeClass);c.data("options",r);var C=e.element("
").addClass("fancymodal-content");if(r.showCloseButton){var g=e.element("
").addClass("fancymodal-close");C.append(g)}r.closeOnEscape&&f.bind("keydown",u);var $=e.element("
").addClass("fancymodal-data");if(r.overlay){var b=e.element("
").addClass("fancymodal-overlay").addClass(r.openingOverlayClass);c.append(b)}C.append($),C.addClass(r.openingClass),c.bind("click",t),c.append(C),x.append(s(c)(d));var A="fancymodal-"+E,D={id:A,close:function(){return k(A)}},M=i(D);return e.extend({opened:M},D)}function k(e){var o=n.getElementById(e);o?g(o):$()}var w=0,E=0,O=!1,A=(n.body||n.documentElement).style,D=e.isDefined(A.animation)||e.isDefined(A.WebkitAnimation)||e.isDefined(A.MozAnimation)||e.isDefined(A.MsAnimation)||e.isDefined(A.OAnimation),M="animationend webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend",x=e.element(n).find("body"),j=e.element(n).find("html");return{open:function(e){return b(e)},close:function(e){return k(e)},getDefaults:function(){return e.copy(o)}}}]}).directive("fancyModal",["$fancyModal",function(n){return{restrict:"A",link:function(o,a,t){var l,i;a.on(t.trigger||"click",function(){i=n.getDefaults(),e.forEach(Object.keys(i),function(n){e.isDefined(t[n])&&(i[n]="true"===t[n]?!0:"false"===t[n]?!1:t[n])}),l=n.open(i)}),o.$on("$destroy",function(){l?l.opened.then(function(){l.close(),l=null}):l=null,i=null})}}}])}(angular,document,window); -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.element(document).ready(function() { 5 | angular.bootstrap(document, ['app']); 6 | }); 7 | 8 | function config($fancyModalProvider) { 9 | $fancyModalProvider.setDefaults({ 10 | template: '
I\'m a basic template
' 11 | }); 12 | } 13 | 14 | function SecondCtrl($scope, $fancyModal) { 15 | $scope.dialogModel = { 16 | message: 'message from passed scope' 17 | }; 18 | $scope.openSecond = function() { 19 | $fancyModal.open({ 20 | template: '

Close all by click here!

', 21 | plain: true, 22 | closeByEscape: false, 23 | controller: 'SecondModalCtrl' 24 | }); 25 | }; 26 | } 27 | 28 | function ThirdCtrl($scope, $fancyModal) { 29 | $scope.closeSecond = function() { 30 | $fancyModal.close(); 31 | }; 32 | } 33 | 34 | function ResolveCtrl($scope, items) { 35 | $scope.items = items; 36 | $scope.close = $scope.$modal.close; 37 | } 38 | 39 | function CtrlAs($scope) { 40 | var crtl = this; 41 | crtl.close = $scope.$modal.close; 42 | } 43 | 44 | function AppCtrl($rootScope, $scope, $http, $q, $timeout, $fancyModal) { 45 | $scope.data = {}; 46 | $scope.data.defaultMessage = 'hello fancy modal!'; 47 | 48 | $rootScope.$on('$fancyModal.opened', function (e, $modal) { 49 | console.log('$fancyModal opened: ' + $modal.attr('id')); 50 | }); 51 | 52 | $rootScope.$on('$fancyModal.closed', function (e, id) { 53 | console.log('$fancyModal closed: ' + id); 54 | }); 55 | 56 | var app = this; 57 | 58 | app.openWithresolve = function() { 59 | $fancyModal.open({ 60 | templateUrl: 'resolve-template.html', 61 | controller: 'ResolveCtrl', 62 | resolve: { 63 | items: function() { 64 | var deferred = $q.defer(); 65 | $timeout(function() { 66 | var data = ['async', 'data', 'loaded']; 67 | deferred.resolve(data); 68 | }, 1500); 69 | return deferred.promise; 70 | } 71 | } 72 | }); 73 | }; 74 | 75 | app.openDefault = function() { 76 | $fancyModal.open(); 77 | }; 78 | 79 | app.openThemed = function() { 80 | $fancyModal.open({ 81 | themeClass: 'fancymodal-theme-classic' 82 | }); 83 | }; 84 | 85 | 86 | app.openWithAnimateCss = function() { 87 | $fancyModal.open({ 88 | template: '
{{data.defaultMessage}}
', 89 | scope: $scope, 90 | openingClass: 'animated rollIn', 91 | closingClass: 'animated rollOut', 92 | openingOverlayClass: 'animated fadeIn', 93 | closingOverlayClass: 'animated fadeOut', 94 | }); 95 | }; 96 | 97 | app.openWithAnimateCss2 = function() { 98 | $fancyModal.open({ 99 | template: '
I\'m a basic template, opened and closed with animate.css effects
', 100 | openingClass: 'animated zoomIn', 101 | closingClass: 'animated hinge', 102 | openingOverlayClass: 'animated fadeIn', 103 | closingOverlayClass: 'animated fadeOut', 104 | }); 105 | }; 106 | 107 | 108 | app.openWithoutOverlay = function() { 109 | $fancyModal.open({ 110 | overlay: false 111 | }); 112 | }; 113 | 114 | app.avoidEscape = function() { 115 | $fancyModal.open({ 116 | template: '
You cannot close my pressing escape or clicking on overlay.
', 117 | closeOnEscape: false, 118 | closeOnOverlayClick: false 119 | }); 120 | }; 121 | 122 | app.openInlineController = function() { 123 | $fancyModal.open({ 124 | template: '
{{message}}
', 125 | controller: ['$scope', function($scope) { 126 | $scope.message = 'Hello, this string is declared in the controller.'; 127 | }] 128 | }); 129 | }; 130 | 131 | app.openWithLoadingMessage = function() { 132 | $fancyModal.open({ 133 | templateUrl: 'loading.html', 134 | controller: ['$scope', function($scope) { 135 | $timeout(function() { 136 | $scope.data = 'data has been loaded!'; 137 | }, 2000); 138 | }] 139 | }); 140 | }; 141 | 142 | app.openControllerAsSyntax = function() { 143 | $fancyModal.open({ 144 | templateUrl: 'controller-as.html', 145 | controller: 'CtrlAs as ctrl', 146 | scope: $scope 147 | }); 148 | }; 149 | 150 | app.openTimed = function() { 151 | var modal = $fancyModal.open({ 152 | template: '

I\'ll close in 2 seconds!

', 153 | closeOnEscape: false, 154 | closeOnOverlayClick: false 155 | }); 156 | setTimeout(function() { 157 | modal.close(); 158 | }, 2000); 159 | }; 160 | 161 | app.openAndClosePromise = function() { 162 | var modal = $fancyModal.open({ 163 | templateUrl: 'resolve-template.html', 164 | controller: 'ResolveCtrl', 165 | resolve: { 166 | items: function() { 167 | var deferred = $q.defer(); 168 | $timeout(function() { 169 | var data = ['async', 'data', 'loaded']; 170 | deferred.resolve(data); 171 | }, 1500); 172 | return deferred.promise; 173 | } 174 | } 175 | }); 176 | console.log(modal); 177 | modal.opened.then(function() { 178 | modal.close(); 179 | }); 180 | }; 181 | 182 | } 183 | 184 | 185 | 186 | angular.module('app', [ 187 | 'vesparny.fancyModal' 188 | ]) 189 | .config(config) 190 | .controller('AppCtrl', AppCtrl) 191 | .controller('ResolveCtrl', ResolveCtrl) 192 | .controller('CtrlAs', CtrlAs) 193 | .controller('SecondCtrl', SecondCtrl) 194 | .controller('ThirdCtrl', ThirdCtrl); 195 | 196 | })(); 197 | -------------------------------------------------------------------------------- /example/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesparny/angular-fancy-modal/7947e57be9388e668b7edab09d4c0205948cb052/example/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /example/controller-as.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{data.defaultMessage}} 4 |
5 |
6 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /example/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesparny/angular-fancy-modal/7947e57be9388e668b7edab09d4c0205948cb052/example/favicon.ico -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | 28 |
29 |
30 |
31 |
    32 |
  • 33 | 34 |
  • 35 |
  • 36 | 37 |
  • 38 |
  • 39 | Tweet 41 |
  • 42 |
43 |
44 |
45 | 83 |
84 |
85 | 104 |
105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /example/loading.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
loading...
4 | {{data}} 5 |
6 | 7 |
8 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /example/long.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris vulputate quam erat, quis mollis turpis pellentesque eget. Praesent pretium eros vitae malesuada ullamcorper. Quisque vel velit mattis libero congue feugiat. Quisque convallis hendrerit dolor, 4 | eget dignissim massa sodales non. Duis interdum convallis elit, vestibulum dapibus risus. Cras id gravida nisi. Cras quis dui tincidunt, venenatis eros ac, elementum quam. Cras euismod mauris mauris. Quisque sed luctus enim. Etiam volutpat neque vel 5 | mauris vestibulum vulputate. Sed pretium interdum auctor. Nam purus sem, molestie quis quam sit amet, eleifend tincidunt ex. Sed turpis leo, convallis ac lacinia quis, maximus eget eros. Nunc justo augue, interdum in tincidunt at, fermentum id urna. 6 | Mauris vel nisl luctus, lobortis odio dictum, tempor nibh. Aliquam vel metus sem. Phasellus tellus arcu, tincidunt eget tincidunt a, posuere eu orci. Proin nec ante posuere, fringilla enim sed, vehicula erat. Maecenas sodales enim nisl. Donec dictum 7 | lectus vitae enim volutpat dapibus. Duis semper urna vel neque feugiat gravida. Etiam luctus nulla justo, sit amet porttitor nibh mollis ut. Ut luctus ullamcorper nisi at aliquet. Nulla ac vehicula enim, a viverra risus. Praesent venenatis felis dui. 8 | Praesent sed interdum tellus, id tempus massa. Aenean eget lectus sit amet odio varius mollis. Donec ac ipsum vel leo mollis blandit. Sed iaculis finibus elementum. Nullam maximus lorem nec neque hendrerit, aliquam auctor urna faucibus. Nunc lectus 9 | neque, congue sed mi a, dignissim pharetra libero. Fusce gravida, sapien quis blandit finibus, massa orci pellentesque est, eget luctus nisi tellus nec magna. Praesent et urna interdum, commodo nisi quis, sodales felis. Quisque erat nulla, iaculis 10 | at arcu ac, lobortis luctus ex. Phasellus viverra odio in scelerisque posuere. Maecenas sollicitudin nibh ac tortor viverra dignissim. Aliquam lobortis sollicitudin libero ut pulvinar. Duis dapibus velit quis orci tincidunt, eu bibendum ligula viverra. 11 | Nam ullamcorper justo molestie mi dictum, a ultrices massa feugiat. Etiam laoreet lacus eu massa gravida ullamcorper. Ut ultricies, lectus eget placerat ullamcorper, dolor nibh luctus tortor, et accumsan metus nisi quis orci. Curabitur pharetra velit 12 | sapien, et vestibulum dolor dapibus non. In et ipsum diam. Praesent ut quam id erat iaculis condimentum. Sed auctor quam consectetur dui placerat, in consequat orci mollis. Nunc vel tellus ut sapien semper tempus. Nullam erat leo, rutrum sed convallis 13 | a, malesuada in justo. Vestibulum sed luctus turpis. Suspendisse bibendum vulputate arcu, sit amet ultrices tortor ullamcorper sagittis. Nunc rhoncus tellus viverra ligula aliquet hendrerit. 14 |
15 | 16 |
17 | 18 |
19 |
20 | -------------------------------------------------------------------------------- /example/main.css: -------------------------------------------------------------------------------- 1 | html, 2 | button, 3 | input, 4 | select, 5 | textarea, 6 | .pure-g [class*="pure-u"] { 7 | font-family: 'Open Sans', sans-serif 8 | } 9 | body { 10 | position: relative; 11 | background-color: #dddddd; 12 | } 13 | header .pure-menu.pure-menu-horizontal { 14 | background-color: #ff851b; 15 | } 16 | html { 17 | font-size: 16px; 18 | font-size: 1rem; 19 | line-height: 1.5; 20 | } 21 | hr3 { 22 | background: #eee; 23 | padding: 4px; 24 | border-radius: 3px; 25 | } 26 | .example a { 27 | display: inline-block; 28 | padding: 10px; 29 | } 30 | footer { 31 | background-color: #fff; 32 | } 33 | .page { 34 | text-align: center; 35 | } 36 | a:active { 37 | outline: none; 38 | -moz-outline-style: none; 39 | } 40 | a:focus { 41 | outline: none; 42 | -moz-outline-style: none; 43 | } 44 | ::-moz-selection { 45 | background: #b3d4fc; 46 | text-shadow: none 47 | } 48 | ::selection { 49 | background: #b3d4fc; 50 | text-shadow: none 51 | } 52 | ::-webkit-input-placeholder { 53 | color: #999 54 | } 55 | ::-moz-placeholder { 56 | color: #999 57 | } 58 | :-ms-input-placeholder { 59 | color: #999 60 | } 61 | img.pure-img { 62 | vertical-align: middle; 63 | max-width: 60%; 64 | display: inline 65 | } 66 | code { 67 | color: #ff4136 68 | } 69 | h3 { 70 | font-weight: 400; 71 | font-size: 22px; 72 | font-size: 1.375rem 73 | } 74 | a { 75 | color: #0074d9; 76 | text-decoration: none; 77 | line-height: inherit 78 | } 79 | a:focus, 80 | a:hover { 81 | color: #005bab 82 | } 83 | a.pure-button:focus, 84 | a.pure-button:hover { 85 | color: #fff 86 | } 87 | pre code { 88 | color: #111; 89 | background-color: #ddd; 90 | border-width: 1px; 91 | border-style: solid; 92 | border-color: #aaa; 93 | display: block; 94 | padding: .5em 95 | } 96 | h4 { 97 | font-size: 21px; 98 | font-size: 1.3125rem 99 | } 100 | .text-center, 101 | .hlist { 102 | text-align: center 103 | } 104 | .island { 105 | padding: 1em 106 | } 107 | .island3 { 108 | padding: 3em 109 | } 110 | .island-panel { 111 | background-color: #ddd; 112 | padding: 2em; 113 | font-size: 20px; 114 | font-size: 1.25rem 115 | } 116 | .hlist { 117 | text-align: center list-style: none; 118 | margin: 0; 119 | padding: 0; 120 | border: 0 none; 121 | } 122 | .hlist>li { 123 | display: inline-block; 124 | margin-left: 1em 125 | } 126 | .hlist>li a.tweet { 127 | position: relative; 128 | top: -3px 129 | } 130 | .button-xsmall { 131 | font-size: 70% 132 | } 133 | .button-small { 134 | font-size: 85% 135 | } 136 | .button-large { 137 | font-size: 110% 138 | } 139 | .button-xlarge { 140 | font-size: 125% 141 | } 142 | .button-expanded { 143 | width: 100%; 144 | padding: 1rem 0; 145 | margin: 3px 146 | } 147 | .l-page .pure-g > div { 148 | -webkit-box-sizing: border-box; 149 | -moz-box-sizing: border-box; 150 | box-sizing: border-box 151 | } 152 | .center { 153 | margin: 0 auto; 154 | } 155 | .text-center { 156 | text-align: center; 157 | } 158 | .button-success, 159 | .button-error, 160 | .button-warning, 161 | .button-secondary { 162 | color: white; 163 | border-radius: 4px; 164 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); 165 | } 166 | .button-success { 167 | background: rgb(28, 184, 65); 168 | /* this is a green */ 169 | } 170 | .button-error { 171 | background: rgb(202, 60, 60); 172 | /* this is a maroon */ 173 | } 174 | .button-warning { 175 | background: rgb(223, 117, 20); 176 | /* this is an orange */ 177 | } 178 | .button-secondary { 179 | background: rgb(66, 184, 221); 180 | /* this is a light blue */ 181 | } 182 | -------------------------------------------------------------------------------- /example/resolve-template.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | {{ item }} 7 |
8 |
9 |
10 |
11 |
12 | 13 |
14 |
15 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var config = require('./build.config.js'); 4 | var karmaConfig = require('./karma.config.js'); 5 | var gulp = require('gulp'); 6 | var $ = require('gulp-load-plugins')(); 7 | var runSequence = require('run-sequence'); 8 | var browserSync = require('browser-sync'); 9 | var pkg = require('./package'); 10 | var karma = require('karma').server; 11 | var _ = require('lodash'); 12 | 13 | 14 | // run unit tests with travis CI 15 | gulp.task('travis', function(cb) { 16 | karma.start(_.assign({}, karmaConfig, { 17 | singleRun: true, 18 | browsers: ['PhantomJS'] 19 | }), cb); 20 | }); 21 | 22 | //generate css files from scss sources 23 | gulp.task('sass', function() { 24 | return gulp.src(config.scss) 25 | .pipe($.header(config.banner, { 26 | pkg: pkg 27 | })) 28 | .pipe($.sass()) 29 | .on('error', function(err) { 30 | console.log(err.message); 31 | }) 32 | .pipe(gulp.dest(config.dist)) 33 | .pipe($.csso()) 34 | .pipe($.header(config.banner, { 35 | pkg: pkg 36 | })) 37 | .pipe($.rename({ 38 | suffix: '.min' 39 | })) 40 | .pipe(gulp.dest(config.dist)); 41 | }); 42 | 43 | //generate js files 44 | gulp.task('js', function() { 45 | return gulp.src(config.js) 46 | .pipe($.header(config.banner, { 47 | pkg: pkg 48 | })) 49 | .pipe(gulp.dest(config.dist)) 50 | .pipe($.uglify()) 51 | .pipe($.rename({ 52 | suffix: '.min' 53 | })) 54 | .pipe($.header(config.banner, { 55 | pkg: pkg 56 | })) 57 | .pipe(gulp.dest(config.dist)); 58 | }); 59 | 60 | //build files 61 | gulp.task('build', function(cb) { 62 | runSequence(['sass', 'js'], cb); 63 | }); 64 | 65 | //lint files 66 | gulp.task('jshint', function() { 67 | return gulp.src(config.js) 68 | .pipe($.jshint()).on('error', function(err){ 69 | console.log(err); 70 | }) 71 | .pipe($.jshint.reporter('jshint-stylish')); 72 | }); 73 | 74 | /* tasks supposed to be public */ 75 | 76 | //default task 77 | gulp.task('default', ['serve']); // 78 | 79 | //run unit tests and exit 80 | gulp.task('test', function(cb) { 81 | karma.start(_.assign({}, karmaConfig, { 82 | singleRun: true 83 | }), cb); 84 | }); 85 | 86 | //run the server after having built generated files, and watch for changes 87 | gulp.task('serve', function() { 88 | browserSync({ 89 | logLevel: 'silent', 90 | notify: false, 91 | server: ['example', '.'] 92 | }); 93 | 94 | gulp.watch(config.scss, ['sass']); 95 | gulp.watch(config.js, ['jshint', 'js']); 96 | }); 97 | 98 | gulp.task('bump', function() { 99 | gulp.src(['./bower.json', './package.json']) 100 | .pipe($.bump()) 101 | .pipe(gulp.dest('.')); 102 | }); 103 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /karma.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | module.exports = { 5 | 6 | //This is the list of file patterns to load into the browser during testing. 7 | files: [ 8 | 'bower_components/jquery/dist/jquery.js', 9 | 'bower_components/angular/angular.js', 10 | 'bower_components/angular-mocks/angular-mocks.js', 11 | 'lib/*.js', 12 | 'test/*.js' 13 | ], 14 | 15 | //used framework 16 | frameworks: ['jasmine'], 17 | 18 | plugins: [ 19 | 'karma-chrome-launcher', 20 | 'karma-phantomjs-launcher', 21 | 'karma-mocha-reporter', 22 | 'karma-jasmine', 23 | 'karma-coverage' 24 | ], 25 | 26 | preprocessors: { 27 | '**/lib/*.js': 'coverage' 28 | }, 29 | 30 | reporters: ['mocha', 'coverage'], 31 | 32 | coverageReporter: { 33 | type: 'html', 34 | dir: 'unit-results/coverage', 35 | file: 'coverage.html' 36 | }, 37 | 38 | logLevel: 'info', 39 | 40 | urlRoot: '/__test/', 41 | 42 | //used browsers (overriddeng in some gulp task) 43 | browsers: ['Chrome'], 44 | 45 | }; 46 | -------------------------------------------------------------------------------- /lib/angular-fancy-modal-theme-classic.scss: -------------------------------------------------------------------------------- 1 | .fancymodal.fancymodal-theme-classic { 2 | .fancymodal-content { 3 | max-width: 420px; 4 | background-color: #000; 5 | color: #fff; 6 | } 7 | 8 | .fancymodal-content-opening, { 9 | -webkit-animation: fancymodal-in 1s; 10 | animation: fancymodal-in 1s; 11 | } 12 | .fancymodal-content-closing { 13 | -webkit-animation: fancymodal-out 1s; 14 | animation: fancymodal-out 1s; 15 | } 16 | .fancymodal-overlay-opening { 17 | -webkit-animation: fancymodal-fadeIn 1s; 18 | animation: fancymodal-fadeIn 1s; 19 | } 20 | .fancymodal-overlay-closing { 21 | -webkit-animation: fancymodal-fadeOut 1s; 22 | animation: fancymodal-fadeOut 1s; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /lib/angular-fancy-modal.js: -------------------------------------------------------------------------------- 1 | (function(angular, document, window, undefined) { 2 | 'use strict'; 3 | angular.module('vesparny.fancyModal', []). 4 | provider('$fancyModal', function() { 5 | var defaults = this.defaults = { 6 | templateUrl: '', 7 | template: '
', 8 | resolve: {}, 9 | controller: '', 10 | scope: '', 11 | showCloseButton: true, 12 | closeOnEscape: true, 13 | closeOnOverlayClick: true, 14 | overlay: true, 15 | themeClass: '', 16 | openingClass: 'fancymodal-content-opening', 17 | closingClass: 'fancymodal-content-closing', 18 | openingOverlayClass: 'fancymodal-overlay-opening', 19 | closingOverlayClass: 'fancymodal-overlay-closing', 20 | bodyClass: 'fancymodal-open' 21 | }; 22 | 23 | /** 24 | * [setDefaults overriding defaults] 25 | * @param {[Object]} options [new defaults] 26 | */ 27 | this.setDefaults = function(options) { 28 | angular.extend(this.defaults, options); 29 | }; 30 | 31 | this.$get = ['$controller', '$timeout', '$rootScope', '$injector', '$compile', '$http', '$templateCache', '$window', '$document', '$q', 32 | function($controller, $timeout, $rootScope, $injector, $compile, $http, $templateCache, $window, $document, $q) { 33 | var modalCounter = 0; 34 | var incrementalId = 0; 35 | var isClosing = false; 36 | var style = (document.body || document.documentElement).style; 37 | var animationEndSupport = angular.isDefined(style.animation) || angular.isDefined(style.WebkitAnimation) || angular.isDefined(style.MozAnimation) || angular.isDefined(style.MsAnimation) || angular.isDefined(style.OAnimation); 38 | var animationEndEvent = 'animationend webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend'; 39 | var $body = angular.element(document).find('body'); 40 | var $html = angular.element(document).find('html'); 41 | 42 | // private methds 43 | 44 | function onKeydown(event) { 45 | if (event.keyCode === 27) { 46 | close(); 47 | } 48 | } 49 | 50 | //http://davidwalsh.name/detect-scrollbar-width 51 | function getScrollBarWidth() { 52 | var scrollDiv = document.createElement('div'); 53 | scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;'; 54 | document.body.appendChild(scrollDiv); 55 | var size = scrollDiv.offsetWidth - scrollDiv.clientWidth; 56 | document.body.removeChild(scrollDiv); 57 | return size; 58 | } 59 | 60 | function hasScrollbars() { 61 | return $body[0].scrollHeight > $window.innerHeight; 62 | } 63 | 64 | function getTemplatePromise(options) { 65 | var deferred = $q.defer(); 66 | if (options.templateUrl) { 67 | $http.get(options.templateUrl, { 68 | cache: $templateCache 69 | }).then(function(result) { 70 | deferred.resolve(result.data); 71 | }); 72 | } else { 73 | deferred.resolve(options.template); 74 | } 75 | return deferred.promise; 76 | } 77 | 78 | function getResolvePromises(resolves) { 79 | var promisesArr = []; 80 | angular.forEach(resolves, function(value) { 81 | promisesArr.push($q.when($injector.invoke(value))); 82 | }); 83 | return promisesArr; 84 | } 85 | 86 | function cleanUp($modal) { 87 | var elementId = $modal.attr('id'); 88 | $modal.scope().$destroy(); 89 | $modal.remove(); 90 | isClosing = false; 91 | if (modalCounter === 0) { 92 | $body.removeClass('fancymodal-open'); 93 | $html.css('margin-right', ''); 94 | } 95 | $rootScope.$emit('$fancyModal.closed', elementId); 96 | } 97 | 98 | function closeModal(modal) { 99 | var $modal = angular.element(modal); 100 | var options = $modal.data('options'); 101 | isClosing = true; 102 | $modal.unbind('click'); 103 | 104 | if (modalCounter === 1) { 105 | $document.unbind('keydown', onKeydown); 106 | } 107 | 108 | modalCounter -= 1; 109 | 110 | if (animationEndSupport) { 111 | var content = angular.element(modal.children[0]); 112 | if (options.overlay) { 113 | var overlay = angular.element(modal.children[0]); 114 | overlay.addClass(options.closingOverlayClass); 115 | content = angular.element(modal.children[1]); 116 | } 117 | content.unbind(animationEndEvent).bind(animationEndEvent, function() { 118 | content.remove(); 119 | cleanUp($modal); 120 | }).removeClass(options.openingClass).addClass(options.closingClass); 121 | } else { 122 | cleanUp($modal); 123 | } 124 | } 125 | 126 | function closeAll() { 127 | var modals = document.querySelectorAll('.fancymodal'); 128 | angular.forEach(modals, function(modal) { 129 | closeModal(modal); 130 | }); 131 | } 132 | 133 | // public API 134 | 135 | /** 136 | * open a modal 137 | * @param {[object]} opts [modal params] 138 | * @return Promise 139 | */ 140 | function open(opts) { 141 | 142 | function closeByAction(event) { 143 | var overlay = angular.element(event.target).hasClass('fancymodal-overlay'); 144 | var closeBtn = angular.element(event.target).hasClass('fancymodal-close'); 145 | 146 | if ((overlay && options.closeOnOverlayClick) || closeBtn) { 147 | close(modal.attr('id')); 148 | } 149 | } 150 | 151 | function execOpen($modal) { 152 | var defer = $q.defer(); 153 | var htmlTemplate; 154 | getTemplatePromise(options).then(function(template) { 155 | htmlTemplate = template; 156 | return $q.all(getResolvePromises(options.resolve)); 157 | }).then(function(locals) { 158 | if (!isClosing) { 159 | var data = {}; 160 | var resolveCounter = 0; 161 | var ctrl; 162 | angular.forEach(options.resolve, function(value, key) { 163 | data[key] = locals[resolveCounter]; 164 | resolveCounter += 1; 165 | }); 166 | scope.$modal = $modal; 167 | data.$scope = scope; 168 | if (options.controller) { 169 | ctrl = $controller(options.controller, data); 170 | } 171 | contentData.append($compile(htmlTemplate)(scope)); 172 | $rootScope.$emit('$fancyModal.opened', modal); 173 | defer.resolve(); 174 | } else { 175 | defer.reject(); 176 | } 177 | }); 178 | return defer.promise; 179 | } 180 | 181 | var options = angular.copy(defaults); 182 | opts = opts || {}; 183 | angular.extend(options, opts); 184 | modalCounter += 1; 185 | incrementalId += 1; 186 | 187 | $body.addClass(options.bodyClass); 188 | 189 | if (hasScrollbars()) { 190 | $html.css('margin-right', getScrollBarWidth() + 'px'); 191 | } 192 | 193 | var scope = (options.scope || $rootScope).$new(); 194 | var modal = angular.element('
').addClass(options.themeClass); 195 | modal.data('options', options); 196 | var content = angular.element('
').addClass('fancymodal-content'); 197 | 198 | if (options.showCloseButton) { 199 | var closeButton = angular.element('
').addClass('fancymodal-close'); 200 | content.append(closeButton); 201 | } 202 | 203 | if (options.closeOnEscape) { 204 | $document.bind('keydown', onKeydown); 205 | } 206 | 207 | var contentData = angular.element('
').addClass('fancymodal-data'); 208 | 209 | if (options.overlay) { 210 | var overlay = angular.element('
').addClass('fancymodal-overlay').addClass(options.openingOverlayClass); 211 | modal.append(overlay); 212 | } 213 | 214 | content.append(contentData); 215 | content.addClass(options.openingClass); 216 | 217 | modal.bind('click', closeByAction); 218 | modal.append(content); 219 | $body.append($compile(modal)(scope)); 220 | var id = 'fancymodal-'+incrementalId; 221 | var $modal = { 222 | id: id, 223 | close: function() { 224 | return close(id); 225 | } 226 | }; 227 | // open the dialog 228 | var openPromise = execOpen($modal); 229 | 230 | return angular.extend({ 231 | opened: openPromise 232 | }, $modal); 233 | } 234 | /** 235 | * [close close a modal] 236 | * @param {[integer]} id [the id of the modal to be closed] 237 | */ 238 | function close(id) { 239 | var modal = document.getElementById(id); 240 | if (modal) { 241 | closeModal(modal); 242 | } else { 243 | closeAll(); 244 | } 245 | } 246 | 247 | return { 248 | open: function(options) { 249 | return open(options); 250 | }, 251 | close: function(id) { 252 | return close(id); 253 | }, 254 | getDefaults: function() { 255 | return angular.copy(defaults); 256 | } 257 | }; 258 | } 259 | ]; 260 | }).directive('fancyModal', ['$fancyModal', function($fancyModal) { 261 | return { 262 | restrict: 'A', 263 | link: function(scope, element, attrs) { 264 | var modal; 265 | var options; 266 | // Trigger 267 | element.on(attrs.trigger || 'click', function() { 268 | options = $fancyModal.getDefaults(); 269 | angular.forEach(Object.keys(options), function(key) { 270 | if (angular.isDefined(attrs[key])) { 271 | //TODO fix this, it sucks bad 272 | if (attrs[key] === 'true') { 273 | options[key] = true; 274 | } else if (attrs[key] === 'false') { 275 | options[key] = false; 276 | } else { 277 | options[key] = attrs[key]; 278 | } 279 | } 280 | }); 281 | modal = $fancyModal.open(options); 282 | }); 283 | 284 | // Garbage collection 285 | scope.$on('$destroy', function() { 286 | if (modal) { 287 | modal.opened.then(function() { 288 | modal.close(); 289 | modal = null; 290 | }); 291 | } else { 292 | modal = null; 293 | } 294 | options = null; 295 | }); 296 | } 297 | }; 298 | }]); 299 | })(angular, document, window); 300 | -------------------------------------------------------------------------------- /lib/angular-fancy-modal.scss: -------------------------------------------------------------------------------- 1 | body.fancymodal-open { 2 | overflow: hidden; 3 | 4 | } 5 | .fancymodal, 6 | .fancymodal *, 7 | .fancymodal *:before, 8 | .fancymodal *:after { 9 | -webkit-box-sizing: border-box; 10 | -moz-box-sizing: border-box; 11 | box-sizing: border-box; 12 | 13 | } 14 | .fancymodal { 15 | padding-top: 120px; 16 | padding-bottom: 160px; 17 | -webkit-overflow-scrolling: touch; 18 | position: fixed; 19 | overflow: auto; 20 | top: 0; 21 | right: 0; 22 | bottom: 0; 23 | left: 0; 24 | z-index: 1050; 25 | padding-left: 8px; 26 | padding-right: 8px; 27 | .fancymodal-overlay { 28 | -webkit-backface-visibility: hidden; 29 | background: rgba(0, 0, 0, 0.3); 30 | position: fixed; 31 | top: 0; 32 | right: 0; 33 | bottom: 0; 34 | left: 0; 35 | 36 | } 37 | .fancymodal-content { 38 | max-width: 500px; 39 | background: #fff; 40 | -webkit-backface-visibility: hidden; 41 | -webkit-border-radius: 5px; 42 | -moz-border-radius: 5px; 43 | -ms-border-radius: 5px; 44 | -o-border-radius: 5px; 45 | padding: 2em 1em; 46 | border-radius: 5px; 47 | background: #eee; 48 | color: #000; 49 | position: relative; 50 | margin: 0 auto; 51 | } 52 | .fancymodal-close { 53 | border-radius: 3px; 54 | position: absolute; 55 | top: 0; 56 | right: 0; 57 | cursor: pointer; 58 | 59 | } 60 | .fancymodal-close:before { 61 | border-radius: 3px; 62 | position: absolute; 63 | content: "\00D7"; 64 | font-size: 26px; 65 | line-height: 30px; 66 | height: 30px; 67 | width: 30px; 68 | text-align: center; 69 | top: 3px; 70 | right: 3px; 71 | color: #ccc; 72 | } 73 | .fancymodal-close:hover:before, 74 | .fancymodal-close:active:before { 75 | color: #777; 76 | background: #e0e0e0; 77 | } 78 | .fancymodal-content-opening, { 79 | -webkit-animation: fancymodal-in 0.5s; 80 | animation: fancymodal-in 0.5s; 81 | } 82 | .fancymodal-content-closing { 83 | -webkit-animation: fancymodal-out 0.5s; 84 | animation: fancymodal-out 0.5s; 85 | } 86 | .fancymodal-overlay-opening { 87 | -webkit-animation: fancymodal-fadeIn 0.5s; 88 | animation: fancymodal-fadeIn 0.5s; 89 | } 90 | .fancymodal-overlay-closing { 91 | -webkit-animation: fancymodal-fadeOut 0.5s; 92 | animation: fancymodal-fadeOut 0.5s; 93 | } 94 | 95 | } 96 | @-webkit-keyframes fancymodal-in { 97 | 0% { 98 | opacity: 0; 99 | -webkit-transform: translateY(-40px); 100 | transform: translateY(-40px); 101 | } 102 | 103 | 100% { 104 | opacity: 1; 105 | -webkit-transform: translateY(0); 106 | transform: translateY(0); 107 | } 108 | 109 | } 110 | @keyframes fancymodal-in { 111 | 0% { 112 | opacity: 0; 113 | -webkit-transform: translateY(-40px); 114 | transform: translateY(-40px); 115 | 116 | } 117 | 100% { 118 | opacity: 1; 119 | -webkit-transform: translateY(0); 120 | transform: translateY(0); 121 | } 122 | 123 | } 124 | @-webkit-keyframes fancymodal-out { 125 | 0% { 126 | opacity: 1; 127 | -webkit-transform: translateY(0); 128 | transform: translateY(0); 129 | 130 | } 131 | 100% { 132 | opacity: 0; 133 | -webkit-transform: translateY(-40px); 134 | transform: translateY(-40px); 135 | } 136 | 137 | } 138 | @keyframes fancymodal-out { 139 | 0% { 140 | opacity: 1; 141 | -webkit-transform: translateY(0); 142 | transform: translateY(0); 143 | 144 | } 145 | 100% { 146 | opacity: 0; 147 | -webkit-transform: translateY(-40px); 148 | transform: translateY(-40px); 149 | } 150 | } 151 | 152 | @-webkit-keyframes fancymodal-fadeIn { 153 | 0% { 154 | opacity: 0; 155 | 156 | } 157 | 100% { 158 | opacity: 1; 159 | } 160 | 161 | } 162 | @keyframes fancymodal-fadeIn { 163 | 0% { 164 | opacity: 0; 165 | 166 | } 167 | 100% { 168 | opacity: 1; 169 | } 170 | } 171 | 172 | @-webkit-keyframes fancymodal-fadeOut { 173 | 0% { 174 | opacity: 1; 175 | 176 | } 177 | 100% { 178 | opacity: 0; 179 | } 180 | 181 | } 182 | @keyframes fancymodal-fadeOut { 183 | 0% { 184 | opacity: 1; 185 | 186 | } 187 | 100% { 188 | opacity: 0; 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Alessandro Arnodo", 3 | "name": "angular-fancy-modal", 4 | "email": "alessandro@arnodo.net", 5 | "url": "http://alessandro.arnodo.net", 6 | "version": "0.1.4", 7 | "homepage": "https://github.com/vesparny/angular-fancy-modal", 8 | "description": "the definitive modal/popup/dialog solution for AngularJS.", 9 | "license": "MIT", 10 | "bugs": "https://github.com/vesparny/angular-fancy-modal/issues", 11 | "repository": { 12 | "type": "git", 13 | "url": "git@github.com:vesparny/angular-fancy-modal.git" 14 | }, 15 | "devDependencies": { 16 | "browser-sync": "^2.7.1", 17 | "gulp": "^3.8.11", 18 | "gulp-bump": "^0.3.1", 19 | "gulp-csso": "^1.0.0", 20 | "gulp-header": "^1.2.2", 21 | "gulp-jshint": "^1.10.0", 22 | "gulp-load-plugins": "^0.10.0", 23 | "gulp-rename": "^1.2.2", 24 | "gulp-sass": "^2.0.1", 25 | "gulp-uglify": "^1.2.0", 26 | "jshint-stylish": "^1.0.2", 27 | "karma": "0.12.28", 28 | "karma-chrome-launcher": "^0.1.12", 29 | "karma-coverage": "^0.3.1", 30 | "karma-jasmine": "^0.3.5", 31 | "karma-mocha-reporter": "^1.0.2", 32 | "karma-phantomjs-launcher": "^0.1.4", 33 | "lodash": "^3.9.1", 34 | "run-sequence": "^1.1.0" 35 | }, 36 | "engines": { 37 | "node": ">=0.10.x" 38 | }, 39 | "scripts": { 40 | "test": "gulp test" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/angular-fancy-modal.spec.js: -------------------------------------------------------------------------------- 1 | /*jshint undef:false */ 2 | (function() { 3 | 'use strict'; 4 | describe('angular-fancy-modal', function() { 5 | var $fancyModal; 6 | var $rootScope; 7 | var $timeout; 8 | var modal; 9 | 10 | beforeEach(module('vesparny.fancyModal')); 11 | 12 | beforeEach(inject(function(_$rootScope_, _$fancyModal_, $templateCache, _$timeout_) { 13 | $templateCache.put('test.html', [200, '
{{hello}}
', {}]); 14 | $fancyModal = _$fancyModal_; 15 | $rootScope = _$rootScope_; 16 | $timeout = _$timeout_; 17 | $rootScope.hello = 'hello'; 18 | })); 19 | 20 | afterEach(function() { 21 | modal.close(); 22 | angular.element(document.body).empty(); 23 | }); 24 | 25 | it('should return specific properties', function() { 26 | modal = $fancyModal.open(); 27 | expect(typeof modal.close).toEqual('function'); 28 | expect(modal.id).toBeDefined(); 29 | expect(modal.opened).toBeDefined(); 30 | }); 31 | 32 | describe('#open', function() { 33 | it('should create the modal with the right scope value', function() { 34 | modal = $fancyModal.open({ 35 | templateUrl: 'test.html' 36 | }); 37 | $rootScope.$digest(); 38 | expect(angular.element(document.body).find('#' + modal.id + ' .fancymodal-data').text()).toBe('hello'); 39 | }); 40 | 41 | it('should create the modal with the right scope value within the inline controller', function() { 42 | modal = $fancyModal.open({ 43 | template: '
{{hello}}
' 44 | }); 45 | $rootScope.$digest(); 46 | expect(angular.element(document.body).find('#' + modal.id + ' .fancymodal-data').text()).toBe('hello'); 47 | }); 48 | }); 49 | 50 | describe('#close', function() { 51 | it('should remove a modal when close', function() { 52 | modal = $fancyModal.open(); 53 | modal.close(); 54 | //finish DOM rendering 55 | $timeout(function() { 56 | expect(angular.element(document.body).find('#fancymodal-' + modal.id).size()).toBe(0); 57 | }, 0); 58 | }); 59 | 60 | it('should destroy the scope when closed', function() { 61 | var destroySpy = jasmine.createSpy('onDestroy'); 62 | modal = $fancyModal.open({ 63 | controller: function($scope) { 64 | $scope.$on('$destroy', destroySpy); 65 | } 66 | }); 67 | modal.close(); 68 | //finish DOM rendering 69 | $timeout(function() { 70 | expect(destroySpy).toHaveBeenCalled(); 71 | }, 0); 72 | }); 73 | }); 74 | 75 | }); 76 | })(); 77 | --------------------------------------------------------------------------------