├── .gitignore ├── .travis.yml ├── README.md ├── bower.json ├── gulpfile.js ├── karma.conf.js ├── modal.css ├── modal.js ├── modal.min.js ├── modal.min.js.map ├── modal.spec.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | before_script: 5 | - export DISPLAY=:99.0 6 | - sh -e /etc/init.d/xvfb start 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-modal [![Build Status](https://travis-ci.org/btford/angular-modal.png)](https://travis-ci.org/btford/angular-modal) 2 | 3 | A modal factory service for AngularJS that makes it easy to add modals to your app. 4 | 5 | 6 | ## Install 7 | 8 | ```shell 9 | npm install angular-modal 10 | ``` 11 | 12 | ## Usage 13 | 1. Include the `modal.js` script provided by this component into your app. 14 | 2. *Optional:* Include the `modal.css` style provided by this component into your html. 15 | 3. Add `btford.modal` as a module dependency to your app. 16 | 17 | 18 | ## Examples 19 | 20 | [Plunker demo](http://plnkr.co/edit/lJDNqafSCKdpMI8AjR0B?p=preview) 21 | 22 | ### Typical Use 23 | 24 | > app.js 25 | 26 | ```javascript 27 | angular.module('myApp', ['btford.modal']). 28 | 29 | // let's make a modal called `myModal` 30 | factory('myModal', function (btfModal) { 31 | return btfModal({ 32 | controller: 'MyModalCtrl', 33 | controllerAs: 'modal', 34 | templateUrl: 'my-modal.html' 35 | }); 36 | }). 37 | 38 | // typically you'll inject the modal service into its own 39 | // controller so that the modal can close itself 40 | controller('MyModalCtrl', function (myModal) { 41 | this.closeMe = myModal.deactivate; 42 | }). 43 | 44 | controller('MyCtrl', function (myModal) { 45 | this.showModal = myModal.activate; 46 | }); 47 | ``` 48 | 49 | > my-modal.html 50 | 51 | ```html 52 |
53 |

Hello {{name}}

54 |

Close Me

55 |
56 | ``` 57 | 58 | > index.html 59 | 60 | ```html 61 |
62 | Show the modal 63 |
64 | ``` 65 | 66 | ### Cleaning up 67 | 68 | If you add any listeners within the modal's controller that are **outside the modal's `scope`**, 69 | you should remove them with `$scope.$on('$destroy', fn () { ... })` to avoid creating a memory leak. 70 | 71 | Building on the example above: 72 | 73 | > app.js 74 | 75 | ```javascript 76 | // ... 77 | controller('MyModalCtrl', function (myModal, $timeout) { 78 | 79 | var ctrl = this, 80 | timeoutId; 81 | 82 | ctrl.tickCount = 5; 83 | 84 | ctrl.closeMe = function () { 85 | cancelTick(); 86 | myModal.deactivate(); 87 | }; 88 | 89 | function tick() { 90 | timeoutId = $timeout(function() { 91 | ctrl.tickCount -= 1; 92 | if (ctrl.tickCount <= 0) { 93 | ctrl.closeMe(); 94 | } else { 95 | tick(); 96 | } 97 | }, 1000); 98 | } 99 | 100 | function cancelTick() { 101 | $timeout.cancel(timeoutId); 102 | } 103 | 104 | $scope.$on('$destroy', cancelTick); 105 | 106 | tick(); 107 | }). 108 | // ... 109 | ``` 110 | 111 | 112 | ### Inline Options 113 | 114 | **Note:** The best practice is to use a separate file for the template and a separate declaration for 115 | the controller, but inlining these options might be more pragmatic for cases where the template or 116 | controller is just a couple lines. 117 | 118 | ```javascript 119 | angular.module('myApp', []). 120 | 121 | // let's make a modal called myModal 122 | factory('myModal', function (btfModal) { 123 | return btfModal({ 124 | controller: function () { 125 | this.name = 'World'; 126 | }, 127 | controllerAs: 'ctrl', 128 | template: '
Hello {{ctrl.name}}
' 129 | }); 130 | }). 131 | 132 | controller('MyCtrl', function (myModal) { 133 | this.showModal = myModal.activate; 134 | }); 135 | ``` 136 | 137 | ```html 138 |
139 | Show the modal 140 |
141 | ``` 142 | 143 | 144 | ## API 145 | 146 | ### `btfModal` 147 | 148 | The modal `factory`. Takes a configuration object as a parameter: 149 | 150 | ```javascript 151 | var modalService = btfModal({ 152 | /* options */ 153 | }) 154 | ``` 155 | 156 | And returns a `modalService` object that you can use to show/hide the modal (described below). 157 | 158 | The config object **must** either have a `template` or a `templateUrl` option. 159 | 160 | These options work just like the [route configuration in Angular's 161 | `$routeProvider`](http://docs.angularjs.org/api/ngRoute.$routeProvider#methods_when). 162 | 163 | 164 | #### `config.template` 165 | **string:** HTML string of the template to be used for this modal. 166 | Unless the template is very simple, you should probably use `config.templateUrl` instead. 167 | 168 | #### `config.templateUrl` 169 | **string (recommended):** URL to the HTML template to be used for this modal. 170 | 171 | #### `config.controller` 172 | **string|function (optional):** The name of a controller or a controller function. 173 | 174 | #### `config.controllerAs` 175 | **string (optional, recommended):** Makes the controller available on the scope of the modal as the given name. 176 | 177 | #### `config.container` 178 | **DOM Node (optional):** DOM node to prepend . Defaults to `document.body`. 179 | 180 | 181 | ### `modalService` 182 | 183 | A `modalService` has just two methods: `activate` and `deactivate`. 184 | 185 | #### `modalService.activate` 186 | 187 | Takes a hash of objects to add to the scope of the modal as locals. 188 | Adds the modal to the DOM by prepending it to the ``. 189 | Returns a promise that resolves once the modal is active. 190 | 191 | #### `modalService.deactivate` 192 | 193 | Removes the modal (DOM and scope) from the DOM. 194 | Returns a promise that resolves once the modal is removed. 195 | 196 | #### `modalService.active` 197 | 198 | Returns whether or not the modal is currently activated. 199 | 200 | 201 | ## Tests 202 | 203 | You can run the tests with [`karma`](http://karma-runner.github.io/0.10/index.html): 204 | 205 | ```shell 206 | karma start karma.conf.js 207 | ``` 208 | 209 | 210 | ## License 211 | MIT 212 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-modal", 3 | "version": "0.5.0", 4 | "main": "modal.js", 5 | "ignore": [ 6 | "**/.*", 7 | "node_modules", 8 | "components", 9 | "bower_components", 10 | "test", 11 | "tests" 12 | ], 13 | "dependencies": { 14 | "angular": "^1.3.14" 15 | }, 16 | "devDependencies": { 17 | "angular-mocks": "^1.3.14" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | gutil = require('gulp-util'), 3 | uglify = require('gulp-uglify'), 4 | rename = require('gulp-rename'); 5 | 6 | gulp.task('scripts', function() { 7 | return gulp.src('modal.js'). 8 | pipe(rename('modal.min.js')). 9 | pipe(uglify({ 10 | preserveComments: 'some', 11 | outSourceMap: true 12 | })). 13 | pipe(gulp.dest('.')); 14 | }); 15 | 16 | gulp.task('default', function() { 17 | gulp.start('scripts'); 18 | }); 19 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config){ 2 | config.set({ 3 | basePath : './', 4 | 5 | files : [ 6 | 'node_modules/angular/angular.js', 7 | 'node_modules/angular-mocks/angular-mocks.js', 8 | 'modal.js', 9 | 'modal.spec.js' 10 | ], 11 | 12 | autoWatch : true, 13 | 14 | frameworks: ['jasmine'], 15 | 16 | browsers : ['Chrome'], 17 | 18 | plugins : [ 19 | 'karma-junit-reporter', 20 | 'karma-chrome-launcher', 21 | 'karma-firefox-launcher', 22 | 'karma-jasmine' 23 | ], 24 | 25 | junitReporter : { 26 | outputFile: 'test_out/unit.xml', 27 | suite: 'unit' 28 | } 29 | 30 | })} -------------------------------------------------------------------------------- /modal.css: -------------------------------------------------------------------------------- 1 | /* 2 | * angular-modal v0.1.0 3 | * (c) 2013 Brian Ford http://briantford.com 4 | * License: MIT 5 | */ 6 | 7 | .btf-modal { 8 | position: fixed; 9 | top: 50%; 10 | left: 50%; 11 | width: 50%; 12 | max-width: 550px; 13 | min-width: 330px; 14 | height: auto; 15 | z-index: 2000; 16 | -webkit-transform: translateX(-50%) translateY(-50%); 17 | -moz-transform: translateX(-50%) translateY(-50%); 18 | -ms-transform: translateX(-50%) translateY(-50%); 19 | transform: translateX(-50%) translateY(-50%); 20 | } 21 | -------------------------------------------------------------------------------- /modal.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * angular-modal v0.5.0 4 | * (c) 2013 Brian Ford http://briantford.com 5 | * License: MIT 6 | */ 7 | 8 | 'use strict'; 9 | 10 | angular.module('btford.modal', []). 11 | factory('btfModal', ['$animate', '$compile', '$rootScope', '$controller', '$q', '$http', '$templateCache', modalFactoryFactory]); 12 | 13 | function modalFactoryFactory($animate, $compile, $rootScope, $controller, $q, $http, $templateCache) { 14 | return function modalFactory (config) { 15 | if (!(!config.template ^ !config.templateUrl)) { 16 | throw new Error('Expected modal to have exacly one of either `template` or `templateUrl`'); 17 | } 18 | 19 | var template = config.template, 20 | controller = config.controller || null, 21 | controllerAs = config.controllerAs, 22 | container = angular.element(config.container || document.body), 23 | element = null, 24 | html, 25 | scope; 26 | 27 | if (config.template) { 28 | html = $q.when(config.template); 29 | } else { 30 | html = $http.get(config.templateUrl, { 31 | cache: $templateCache 32 | }). 33 | then(function (response) { 34 | return response.data; 35 | }); 36 | } 37 | 38 | function activate (locals) { 39 | return html.then(function (html) { 40 | if (!element) { 41 | attach(html, locals); 42 | } 43 | }); 44 | } 45 | 46 | 47 | function attach (html, locals) { 48 | element = angular.element(html); 49 | if (element.length === 0) { 50 | throw new Error('The template contains no elements; you need to wrap text nodes') 51 | } 52 | scope = $rootScope.$new(); 53 | if (controller) { 54 | if (!locals) { 55 | locals = {}; 56 | } 57 | locals.$scope = scope; 58 | var ctrl = $controller(controller, locals); 59 | if (controllerAs) { 60 | scope[controllerAs] = ctrl; 61 | } 62 | } else if (locals) { 63 | for (var prop in locals) { 64 | scope[prop] = locals[prop]; 65 | } 66 | } 67 | $compile(element)(scope); 68 | return $animate.enter(element, container); 69 | } 70 | 71 | function deactivate () { 72 | if (!element) { 73 | return $q.when(); 74 | } 75 | return $animate.leave(element).then(function () { 76 | scope.$destroy(); 77 | scope = null; 78 | element.remove(); 79 | element = null; 80 | }); 81 | } 82 | 83 | function active () { 84 | return !!element; 85 | } 86 | 87 | return { 88 | activate: activate, 89 | deactivate: deactivate, 90 | active: active 91 | }; 92 | }; 93 | } -------------------------------------------------------------------------------- /modal.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @license 3 | * angular-modal v0.5.0 4 | * (c) 2013 Brian Ford http://briantford.com 5 | * License: MIT 6 | */ 7 | "use strict";function modalFactoryFactory(e,t,n,r,o,a,l){return function(c){function u(e){return p.then(function(t){v||i(t,e)})}function i(o,a){if(v=angular.element(o),0===v.length)throw new Error("The template contains no elements; you need to wrap text nodes");if(d=n.$new(),h){a||(a={}),a.$scope=d;var l=r(h,a);$&&(d[$]=l)}else if(a)for(var c in a)d[c]=a[c];return t(v)(d),e.enter(v,s)}function m(){return v?e.leave(v).then(function(){d.$destroy(),d=null,v.remove(),v=null}):o.when()}function f(){return!!v}if(!(!c.template^!c.templateUrl))throw new Error("Expected modal to have exacly one of either `template` or `templateUrl`");var p,d,h=(c.template,c.controller||null),$=c.controllerAs,s=angular.element(c.container||document.body),v=null;return p=c.template?o.when(c.template):a.get(c.templateUrl,{cache:l}).then(function(e){return e.data}),{activate:u,deactivate:m,active:f}}}angular.module("btford.modal",[]).factory("btfModal",["$animate","$compile","$rootScope","$controller","$q","$http","$templateCache",modalFactoryFactory]); -------------------------------------------------------------------------------- /modal.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"modal.min.js.map","sources":["modal.min.js"],"names":["modalFactoryFactory","$animate","$compile","$rootScope","$controller","$q","$http","$templateCache","config","activate","locals","html","then","element","attach","angular","length","Error","scope","$new","controller","$scope","ctrl","controllerAs","prop","enter","container","deactivate","leave","$destroy","remove","when","active","template","templateUrl","document","body","get","cache","response","data","module","factory"],"mappings":";;;;;;AAOA,YAKA,SAASA,qBAAoBC,EAAUC,EAAUC,EAAYC,EAAaC,EAAIC,EAAOC,GACnF,MAAO,UAAuBC,GAwB5B,QAASC,GAAUC,GACjB,MAAOC,GAAKC,KAAK,SAAUD,GACpBE,GACHC,EAAOH,EAAMD,KAMnB,QAASI,GAAQH,EAAMD,GAErB,GADAG,EAAUE,QAAQF,QAAQF,GACH,IAAnBE,EAAQG,OACV,KAAM,IAAIC,OAAM,iEAGlB,IADAC,EAAQf,EAAWgB,OACfC,EAAY,CACTV,IACHA,MAEFA,EAAOW,OAASH,CAChB,IAAII,GAAOlB,EAAYgB,EAAYV,EAC/Ba,KACFL,EAAMK,GAAgBD,OAEnB,IAAIZ,EACT,IAAK,GAAIc,KAAQd,GACfQ,EAAMM,GAAQd,EAAOc,EAIzB,OADAtB,GAASW,GAASK,GACXjB,EAASwB,MAAMZ,EAASa,GAGjC,QAASC,KACP,MAAKd,GAGEZ,EAAS2B,MAAMf,GAASD,KAAK,WAClCM,EAAMW,WACNX,EAAQ,KACRL,EAAQiB,SACRjB,EAAU,OANHR,EAAG0B,OAUd,QAASC,KACP,QAASnB,EArEX,MAAOL,EAAOyB,UAAYzB,EAAO0B,aAC/B,KAAM,IAAIjB,OAAM,0EAGlB,IAKIN,GACAO,EALAE,GADgBZ,EAAOyB,SACPzB,EAAOY,YAAc,MACrCG,EAAgBf,EAAOe,aACvBG,EAAgBX,QAAQF,QAAQL,EAAOkB,WAAaS,SAASC,MAC7DvB,EAAgB,IAgEpB,OA3DEF,GADEH,EAAOyB,SACF5B,EAAG0B,KAAKvB,EAAOyB,UAEf3B,EAAM+B,IAAI7B,EAAO0B,aACtBI,MAAO/B,IAETK,KAAK,SAAU2B,GACb,MAAOA,GAASC,QAsDlB/B,SAAUA,EACVkB,WAAYA,EACZK,OAAQA,IAhFdjB,QAAQ0B,OAAO,mBACXC,QAAQ,YAAa,WAAY,WAAY,aAAc,cAAe,KAAM,QAAS,iBAAkB1C"} -------------------------------------------------------------------------------- /modal.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('btfModal', function() { 4 | var container, 5 | btfModal, 6 | $rootScope; 7 | 8 | beforeEach(module('btford.modal')); 9 | 10 | beforeEach(function () { 11 | container = angular.element('
'); 12 | }); 13 | 14 | afterEach(function() { 15 | container = null; 16 | }); 17 | 18 | describe('without animations', function () { 19 | beforeEach(inject(function(_btfModal_, _$rootScope_, $templateCache) { 20 | btfModal = _btfModal_; 21 | $rootScope = _$rootScope_; 22 | $rootScope.greeting = 'こんばんは'; 23 | 24 | $templateCache.put('test.html', [200, '
{{greeting}}
', {}]); 25 | })); 26 | 27 | it('should not show a modal initially', function() { 28 | var modal = btfModal({ 29 | templateUrl: 'test.html', 30 | container: container 31 | }); 32 | 33 | $rootScope.$digest(); 34 | 35 | expect(container.text()).toBe(''); 36 | }); 37 | 38 | 39 | it('should throw if called without a `template` or `templateUrl` option', function() { 40 | expect(function () { btfModal({}); }).toThrow(); 41 | }); 42 | 43 | it('should throw if called with a text node', function() { 44 | var modal = btfModal({ 45 | template: 'hey' 46 | }); 47 | expect(function () { 48 | modal.activate(); 49 | $rootScope.$digest(); 50 | }).toThrow(); 51 | }); 52 | 53 | it('should throw if called with both `template` and `templateUrl` options', function() { 54 | expect(function () { 55 | btfModal({ 56 | template: 'foo', 57 | templateUrl: 'foo.html' 58 | }); 59 | }).toThrow(); 60 | }); 61 | 62 | describe('#activate', function () { 63 | it('should show a modal when activated with `templateUrl`', function() { 64 | var modal = btfModal({ 65 | templateUrl: 'test.html', 66 | container: container 67 | }); 68 | modal.activate(); 69 | $rootScope.$digest(); 70 | 71 | expect(container.text()).toBe('こんばんは'); 72 | }); 73 | 74 | it('should show a modal when activated with `template`', function() { 75 | var modal = btfModal({ 76 | template: '{{greeting}}', 77 | container: container 78 | }); 79 | 80 | modal.activate(); 81 | $rootScope.$digest(); 82 | 83 | expect(container.text()).toBe('こんばんは'); 84 | }); 85 | 86 | it('should instantiate a controller via the `controller` option', function() { 87 | var modal = btfModal({ 88 | template: '{{greeting}}', 89 | controller: function ($scope) { 90 | $scope.greeting = 'goodnight' 91 | }, 92 | container: container 93 | }); 94 | 95 | modal.activate(); 96 | $rootScope.$digest(); 97 | 98 | expect(container.text()).toBe('goodnight'); 99 | }); 100 | 101 | it('should expose a controller to the scope via the `controllerAs` option', function() { 102 | var modal = btfModal({ 103 | template: '{{ctrl.greeting}}', 104 | controller: function () { 105 | this.greeting = 'boa noite' 106 | }, 107 | controllerAs: 'ctrl', 108 | container: container 109 | }); 110 | 111 | modal.activate(); 112 | $rootScope.$digest(); 113 | 114 | expect(container.text()).toBe('boa noite'); 115 | }); 116 | 117 | it('should pass locals to the controller scope', function() { 118 | var modal = btfModal({ 119 | template: '{{ctrl.greeting}}', 120 | controller: function (greeting) { 121 | this.greeting = greeting; 122 | }, 123 | controllerAs: 'ctrl', 124 | container: container 125 | }); 126 | 127 | modal.activate({ 128 | greeting: 'おはよう〜' 129 | }); 130 | $rootScope.$digest(); 131 | 132 | expect(container.text()).toBe('おはよう〜'); 133 | }); 134 | 135 | it('should pass locals to the modal scope if there is no controller', function() { 136 | var modal = btfModal({ 137 | template: '{{greeting}}', 138 | container: container 139 | }); 140 | 141 | modal.activate({ 142 | greeting: 'bon soir' 143 | }); 144 | $rootScope.$digest(); 145 | 146 | expect(container.text()).toBe('bon soir'); 147 | }); 148 | 149 | it('should not activate multiple times', function() { 150 | var modal = btfModal({ 151 | template: 'x', 152 | container: container 153 | }); 154 | 155 | modal.activate(); 156 | $rootScope.$digest(); 157 | modal.activate(); 158 | $rootScope.$digest(); 159 | 160 | expect(container.text()).toBe('x'); 161 | }); 162 | 163 | it('should resolve a promise after activating', function() { 164 | var spy = jasmine.createSpy('activated'); 165 | 166 | var modal = btfModal({ 167 | template: 'x', 168 | container: container 169 | }); 170 | 171 | modal.activate().then(spy); 172 | expect(spy).not.toHaveBeenCalled(); 173 | 174 | $rootScope.$digest(); 175 | expect(spy).toHaveBeenCalled(); 176 | }); 177 | }); 178 | 179 | 180 | describe('#deactivate', function () { 181 | it('should remove a modal when deactivated', function() { 182 | 183 | var modal = btfModal({ 184 | template: '{{greeting}}', 185 | container: container 186 | }); 187 | 188 | modal.activate(); 189 | $rootScope.$digest(); 190 | 191 | modal.deactivate(); 192 | $rootScope.$digest(); 193 | 194 | expect(container.text()).toBe(''); 195 | }); 196 | 197 | it('should destroy the scope when deactivated', inject(function($browser) { 198 | var destroySpy = jasmine.createSpy('onDestroy'); 199 | 200 | var modal = btfModal({ 201 | template: '{{greeting}}', 202 | container: container, 203 | controller: function ($scope) { 204 | $scope.$on('$destroy', destroySpy); 205 | } 206 | }); 207 | 208 | modal.activate(); 209 | modal.deactivate().then(destroySpy); 210 | $browser.defer.flush(); 211 | 212 | expect(destroySpy).toHaveBeenCalled(); 213 | })); 214 | 215 | it('should resolve a promise after deactivating', inject(function($browser) { 216 | var spy = jasmine.createSpy('deactivated'); 217 | 218 | var modal = btfModal({ 219 | template: 'x', 220 | container: container 221 | }); 222 | 223 | modal.activate(); 224 | modal.deactivate().then(spy); 225 | $browser.defer.flush(); 226 | 227 | expect(spy).toHaveBeenCalled(); 228 | })); 229 | 230 | }); 231 | 232 | 233 | describe('#active', function () { 234 | it('should return the state of the modal', inject(function($browser) { 235 | 236 | var modal = btfModal({ 237 | template: '{{greeting}}', 238 | container: container 239 | }); 240 | 241 | $rootScope.$digest(); 242 | expect(modal.active()).toBe(false); 243 | 244 | modal.activate(); 245 | $browser.defer.flush(); 246 | expect(modal.active()).toBe(true); 247 | })); 248 | }); 249 | }); 250 | 251 | 252 | describe('with animations', function () { 253 | var $animate, 254 | modal; 255 | 256 | beforeEach(module('ngAnimateMock')); 257 | 258 | beforeEach(inject(function(btfModal, _$rootScope_, _$animate_) { 259 | $rootScope = _$rootScope_; 260 | $animate = _$animate_; 261 | 262 | modal = btfModal({ 263 | template: 'animations!', 264 | container: container 265 | }); 266 | })); 267 | 268 | it('should trigger an enter animation when activated', function () { 269 | modal.activate(); 270 | $rootScope.$digest(); 271 | 272 | var item = $animate.queue.shift(); 273 | expect(item.event).toBe('enter'); 274 | }); 275 | 276 | it('should trigger a leave animation when deactivated', function () { 277 | modal.activate(); 278 | $rootScope.$digest(); 279 | $animate.queue.shift(); 280 | 281 | modal.deactivate(); 282 | $rootScope.$digest(); 283 | 284 | var item = $animate.queue.shift(); 285 | expect(item.event).toBe('leave'); 286 | }); 287 | }); 288 | 289 | }); 290 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-modal", 3 | "version": "0.5.0", 4 | "description": "easily add a modal to your angular app", 5 | "main": "modal.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/karma start karma.conf.js --browsers Firefox --single-run" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/btford/angular-modal.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "angularjs", 16 | "modal" 17 | ], 18 | "author": "Brian Ford", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/btford/angular-modal/issues" 22 | }, 23 | "devDependencies": { 24 | "angular": "^1.3.14", 25 | "angular-mocks": "^1.3.14", 26 | "gulp": "~3.5.5", 27 | "gulp-rename": "~1.2.0", 28 | "gulp-uglify": "~0.2.1", 29 | "gulp-util": "~2.2.14", 30 | "karma": "~0.10", 31 | "karma-junit-reporter": "~0.1.0" 32 | } 33 | } 34 | --------------------------------------------------------------------------------