├── .idea ├── .name ├── Angular-Bootstrap-Modal-Forms.iml ├── misc.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── LICENSE.md ├── README.md ├── anbomofo.js ├── anbomofo.min.js ├── bower.json ├── bower_components ├── angular-bootstrap │ ├── .bower.json │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── bower.json │ ├── index.js │ ├── package.json │ ├── ui-bootstrap-csp.css │ ├── ui-bootstrap-tpls.js │ ├── ui-bootstrap-tpls.min.js │ ├── ui-bootstrap.js │ └── ui-bootstrap.min.js ├── angular-ui-router │ ├── .bower.json │ ├── CHANGELOG.md │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── README.md │ ├── bower.json │ ├── release │ │ ├── angular-ui-router.js │ │ └── angular-ui-router.min.js │ └── src │ │ ├── common.js │ │ ├── resolve.js │ │ ├── state.js │ │ ├── stateDirectives.js │ │ ├── stateFilters.js │ │ ├── templateFactory.js │ │ ├── urlMatcherFactory.js │ │ ├── urlRouter.js │ │ ├── view.js │ │ ├── viewDirective.js │ │ └── viewScroll.js └── angular │ ├── .bower.json │ ├── LICENSE.md │ ├── README.md │ ├── angular-csp.css │ ├── angular.js │ ├── angular.min.js │ ├── angular.min.js.gzip │ ├── angular.min.js.map │ ├── bower.json │ ├── index.js │ └── package.json ├── readmeImages └── Screenshot.png ├── src ├── modalConfig.js ├── modalDirective.js ├── modalService.js └── modalTemplate.html └── test ├── bootstrap.css ├── customers.json ├── index.html ├── testController.js └── testDataService.js /.idea/.name: -------------------------------------------------------------------------------- 1 | Angular-Bootstrap-Modal-Forms -------------------------------------------------------------------------------- /.idea/Angular-Bootstrap-Modal-Forms.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mike Meding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AnBoMoFo (Angular Bootstrap Modal Forms) 2 | A simple, ultra-lightweight AngularJS service which automatically generates data forms and callbacks using a simple JSON model. 3 | 4 | # Live Example 5 | A basic plunker example of this library is now available here 6 | 7 | # Installation 8 | 9 | The only project requirements are AngularJS, AngularBootstrapUI, and UIRouter. 10 | 11 | #### Install with Bower 12 | ```sh 13 | $ bower install anbomofo 14 | ``` 15 | 16 | ### Adding dependency to your project 17 | 18 | When bower is done downloading all of the required files all thats left to do is add dependencies on your main AngularJS module. 19 | 20 | Add the flavor of javascript file you would like. Minified or unminified based on your project. 21 | ```html 22 | 23 | ``` 24 | 25 | Include AngularJS dependency 26 | ```js 27 | angular.module('myModule', ['autoModals']); 28 | ``` 29 | This was tested to work with the following browsers: 30 | * Chrome [43.0.2357.134 (Official Build) (64-bit)] 31 | * Firefox [30.0 Latest Linux Release] 32 | 33 | # Usage 34 | 35 | ![Screenshot](readmeImages/Screenshot.png) 36 | ### Knowledge Requirements 37 | * A good working knowledge of [Angular.js](https://angularjs.org/) and its different componenets. 38 | * A firm grasp of how [Bootstrap](http://www.getbootstrap.com) works. Particularly what modals are and how they work. 39 | * Knowing how to write JSON is also helpful. 40 | 41 | ## Brief Overview 42 | 43 | ### Why? 44 | 45 | When designing a [Angular.js](https://angularjs.org/) application that handles an abundance of form data it can quickly become unmanageable. If you are like me and have a bunch of forms that only differ by a fiew fields it can be infuriating to have to write two separate form templates each with their own set of control methods. This becomes even more of a nightmare when you have 20+ different forms that each have to be collected and updated from a database. In comes this handy library! Angular-Data-Driven-Modals allows you to specify only what the data looks like and like magic you get both a means of adding and updating your data in convient [Bootstrap](http://www.getbootstrap.com) modal forms. 46 | 47 | ### Basic procedure 48 | 49 | * Include this library as a part of your project (Described above) 50 | * Add this library as a dependency to your project (Described above) 51 | * Define a model in this service and allow access to it through a getModel() function 52 | * Define a submit method in this service to gain access to the outgoing data 53 | * Compile your model using the compileModel() method 54 | * Attach the modal to an object in your DOM and PRESTO! 55 | 56 | 57 | ## Include Service 58 | To implement your own data driven modal you need to have a service to support it. The structure of which is as follows, 59 | 60 | ## Model Structure 61 | ```javascript 62 | var onSubmit = function(obj){ 63 | console.log(obj); 64 | }; 65 | 66 | var model = { 67 | // The structure of the input fields 68 | fields: [ 69 | {name: "id", display: false}, // this item is preserved but not displayed 70 | // follows a simalar naming pattern as the input fields would 71 | {name: "name", displayName: "Name", placeholder: "Panel [a,b,etc...]", type: "text", required: true}, 72 | {name: "mac", displayName: "MAC", placeholder: "0x36000...", type: "text", required: true}, 73 | ... 74 | ], 75 | // Add mode settings 76 | addModalSettings: { 77 | title: 'Add New Panel', 78 | // the submission callback function above. All fields are placed into a new object 79 | callback: this.onSubmit 80 | }, 81 | editModalSettings: { 82 | ... 83 | } 84 | }; 85 | 86 | ``` 87 | 88 | ## Implement Method 89 | ```javascript 90 | // this method must be implemented in your angular service 91 | this.getModel = function () { 92 | return model; 93 | }; 94 | ``` 95 | As AngularJS has no means of creating interfaces as in Java you must remember to implement this method on your own. Its function is straightforward as it simply allows public access to your local model variable. 96 | 97 | ## Compile Model 98 | ```javascript 99 | model = ModalService.compileModel(model); 100 | ``` 101 | 102 | ## Attach modal to DOM 103 | ```html 104 | 105 | ``` 106 | This directive uses the existance of the the ng-model="" attribute as its means of differentiating between a modal that is in add mode and edit mode. 107 | 108 | Remember, a basic plunker example of this library is now available here 109 | -------------------------------------------------------------------------------- /anbomofo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Mike Meding 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Change Log: 25 | * Mon Aug 10 2015 Mike Meding 26 | * - initial beta version release 27 | */ 28 | (function () { 29 | "use strict"; 30 | // ======================================================= 31 | // config 32 | // ======================================================= 33 | var module = angular.module('autoModals', ['ui.bootstrap', 'ui.router']); 34 | 35 | // ======================================================= 36 | // directive 37 | // ======================================================= 38 | module.directive('modal', function () { 39 | /** 40 | * Credits and Examples used: 41 | * http://stackoverflow.com/questions/14115701/angularjs-create-a-directive-that-uses-ng-model // directives that use ng-model 42 | * http://stackoverflow.com/questions/20033493/angularjs-passing-service-as-an-argument-to-directive // for argument passing 43 | * http://www.html5rocks.com/en/tutorials/es6/promises/#toc-async // learning about promises 44 | * http://stackoverflow.com/questions/26807524/twitter-bootstrap-3-modal-with-scrollable-body // css modal body scrollbar magic 45 | * http://stackoverflow.com/questions/16529825/angularjs-ngclass-conditional // angular conditional classes for template 46 | * http://stackoverflow.com/questions/12139152/how-to-set-the-value-property-in-angularjs-ng-options // using ngOptions for select tags 47 | * http://odetocode.com/blogs/scott/archive/2013/06/19/using-ngoptions-in-angularjs.aspx // using ngOptions for select tags 48 | */ 49 | 50 | return { 51 | restrict: 'A', 52 | scope: { 53 | ngService: '@', 54 | ngModel: '=' // puts object directly in scope 55 | }, 56 | controller: [ 57 | '$scope', '$attrs', '$injector', '$uibModal', function ($scope, $attrs, $injector, $uibModal) { 58 | // inject service and get button label for this scope 59 | $scope.dataServiceName = $attrs.ngService; 60 | $scope.dataService = $injector.get($scope.dataServiceName); 61 | 62 | /** 63 | * Opens the modal 64 | * Based on the code from: 65 | * http://angular-ui.github.io/bootstrap/#/modal 66 | */ 67 | $scope.openModal = function () { 68 | 69 | function onwards() { 70 | $uibModal.open({ 71 | // this template is constructed from modalTemplate.html by converting the HTML into a giant string. 72 | template: ' ', 73 | controller: 'AddModalInstanceController', 74 | size: 'lg', 75 | resolve: { // this resolves the local vars of Instance Ctrl 76 | ngModel: function () { 77 | return $scope.ngModel; 78 | }, 79 | dataServiceName: function () { // send the modal instance the name of the service that it must inject 80 | return $scope.dataServiceName; 81 | } 82 | } 83 | }); 84 | } 85 | 86 | /** 87 | * if preClick exists execute it 88 | */ 89 | if (typeof $scope.dataService.preClick === "function") { // if preclick function exists 90 | $scope.dataService.preClick(); 91 | } 92 | 93 | 94 | function myTimeout() { 95 | setTimeout(function () { 96 | if (!$scope.ready) { 97 | $scope.ready = $scope.dataService.modelReady(); // if still not ready update 98 | myTimeout(); // run again 99 | } else { 100 | onwards(); // continue execution. 101 | } 102 | }, 100); // 100ms wait 103 | } 104 | 105 | /** 106 | * get relevant data for building our modal from our injected service 107 | * and do not return until that data is ready 108 | */ 109 | if (typeof $scope.dataService.modelReady === 'function') { 110 | $scope.ready = $scope.dataService.modelReady(); 111 | myTimeout(); // kick off synchronous timer 112 | } else { 113 | onwards(); // ignore and continue 114 | } 115 | 116 | 117 | }; 118 | } 119 | ], 120 | link: function compile($scope, element, $attrs, ctrl) { 121 | 122 | // bind the click handler to the element which has our modal-add attribute 123 | element.bind('click', function (e) { 124 | $scope.openModal(); // just open the stupid modal 125 | }); 126 | } 127 | } 128 | }); 129 | /** 130 | * The per instance modal controller. This handles the actual submission of the data 131 | */ 132 | module.controller("AddModalInstanceController", ['$scope', '$uibModalInstance', '$http', '$state', '$injector', 'dataServiceName', 'ngModel', 'ModalService', 133 | function ($scope, $uibModalInstance, $http, $state, $injector, dataServiceName, ngModel, ModalService) { 134 | // using service name to inject the service to our instance controller. 135 | var dataService = $injector.get(dataServiceName); 136 | 137 | var model = dataService.getModel(); 138 | // extract relevant settings 139 | $scope.fields = model['fields']; 140 | if (ngModel === undefined) { 141 | //console.log('add modal'); 142 | $scope.settings = model['addModalSettings']; 143 | } else { 144 | //console.log('edit modal'); 145 | $scope.settings = model['editModalSettings']; 146 | // set field values based on given model 147 | $scope.fields = ModalService.addValuesToFields(ngModel, $scope.fields); 148 | //console.log(ngModel); 149 | //console.log($scope.fields); 150 | } 151 | $scope.title = $scope.settings['title']; 152 | 153 | $scope.cancel = function () {// when cancel button is clicked 154 | $uibModalInstance.dismiss('cancel'); // this causes result promise to fail 155 | }; 156 | 157 | $scope.ok = function (fields) { // when ok button is clicked 158 | var newDataPoint = ModalService.getValuesFromFields(fields); // Extract data from fields 159 | 160 | /** 161 | * Checks only our requirements for validity 162 | */ 163 | var preformValidityCheckAndCallback = function () { // yay for long pointless function names 164 | // only do requirements validation 165 | $scope.fields = ModalService.checkValidRequirements($scope.fields); // call our requirements validation 166 | 167 | if (!ModalService.checkValidFromFields($scope.fields)) { // If any invalid states exist 168 | //console.error('invalid states exist'); 169 | $scope.fields = fields; 170 | } else { 171 | if ($scope.settings.hasOwnProperty('callback')) { 172 | $scope.settings['callback'](newDataPoint); // fire off successful callback function 173 | } 174 | $uibModalInstance.close(); // this causes result promise to succeed and modal to close and reset 175 | } 176 | }; 177 | 178 | // if user validation exists 179 | if ('validate' in $scope.settings) { 180 | $scope.fields = ModalService.resetValidity(fields); // reset validity fields for re-evaluation 181 | $scope.fields = $scope.settings['validate']($scope.fields); // call user defined validation service to update fields 182 | preformValidityCheckAndCallback(); 183 | } else { 184 | $scope.fields = ModalService.resetValidity(fields); // reset validity fields for re-evaluation 185 | preformValidityCheckAndCallback(); 186 | } 187 | }; 188 | 189 | // MODAL PROMISE 190 | $uibModalInstance.result.then(function (results) { // if successful submission 191 | $scope.fields = ModalService.resetModel(dataService.getModel()); // Reset modal 192 | }, function (err) { // if modal fails due to cancel or outside click 193 | $scope.fields = ModalService.resetModel(dataService.getModel()); // Reset modal 194 | }) 195 | 196 | }]); 197 | 198 | // ======================================================= 199 | // service 200 | // ======================================================= 201 | module.service('ModalService', [function () { 202 | /** 203 | * Check that the model has the correct syntax for display 204 | * This serves only as a basic compile. 205 | * @param model 206 | */ 207 | this.compileModel = function (model) { 208 | var compileError = false; 209 | for (var x = 0; x < model['fields'].length; x++) { 210 | var field = model['fields'][x]; // shallow copy 211 | // if dropdown = type then dropdownOptions must also exist and contain at least 1 value 212 | if (field['type'] === 'dropdown' && !('dropdownOptions' in field)) { // if our type is a dropdown 213 | console.error('MODAL ERROR: dropdown field [' + field[name] + '] must have dropdownOptions Array'); 214 | compileError = true; 215 | } 216 | // name must exist 217 | if (!('name' in field)) { 218 | console.error('MODAL ERROR: name field must exist in field [' + JSON.stringify(field) + ']'); 219 | compileError = true; 220 | } 221 | // type must exist if display is true 222 | if (!('type' in field) && field['display']) { 223 | console.error('MODAL ERROR: type field must exist in field [' + JSON.stringify(field) + ']'); 224 | compileError = true; 225 | } 226 | } 227 | if (compileError) { 228 | console.error("Model compile error. Incorrectly formatted model."); 229 | return {}; 230 | } else { 231 | return addDefaultFields(model); // add default fields and return 232 | } 233 | 234 | }; 235 | 236 | /** 237 | * Add all default field parameters if they do not already exist 238 | * @param model 239 | * @returns {*} the updated model 240 | */ 241 | var addDefaultFields = function (model) { 242 | var updatedFields = {}; 243 | for (var x = 0; x < model['fields'].length; x++) { 244 | var field = model['fields'][x]; // shallow copy 245 | if (!('display' in field)) { // if display field does not exist 246 | field['display'] = true; 247 | } 248 | if (!('valid' in field)) { // for validation purposes 249 | field['valid'] = true; 250 | } 251 | if (!('required' in field)) { // if required field does not exist 252 | field['required'] = false; 253 | } 254 | if (!('displayName' in field)) { // if displayName field does not exist 255 | field['displayName'] = field['name']; // use required name field 256 | } 257 | if (!('placeholder' in field)) { // if placeholder field does not exist 258 | field['placeholder'] = 'Please enter a value'; 259 | } 260 | if (field['type'] === 'dropdown') { // if our type is a dropdown 261 | if (!field.hasOwnProperty('value')) { // if no default value is given 262 | field['value'] = field['dropdownOptions'][0]; // its inital value is the first item in dropdown option 263 | } 264 | } 265 | if (field['type'] === 'checkbox') { // if our type is a checkbox 266 | if (!field.hasOwnProperty('value')) { 267 | field['value'] = false; // default is false if nothing else 268 | } 269 | } 270 | if (field['type'] === 'datetime') { // if our type is a date picker 271 | if (!field.hasOwnProperty('value')) { 272 | field.value = new Date(); // default is the current time 273 | } 274 | } 275 | } 276 | return model; 277 | }; 278 | 279 | /** 280 | * Cleans up and returns our model so that it may be used upon reset. 281 | * @param model 282 | */ 283 | this.resetModel = function (model) { 284 | for (var x = 0; x < model['fields'].length; x++) { 285 | var field = model['fields'][x]; // shallow copy 286 | if (field['type'] === 'dropdown') { 287 | field['value'] = field['dropdownOptions'][0]; // its inital value is the first item in dropdown options 288 | } else { 289 | delete field['value'] 290 | } 291 | field['valid'] = true; // reset valid flag 292 | delete field['errorMessage']; // remove any error messages 293 | } 294 | return model; 295 | }; 296 | 297 | 298 | /** 299 | * Returns an object representing all the value fields of the given model 300 | * @param fields is an array of fields 301 | */ 302 | this.getValuesFromFields = function (fields) { 303 | var valueObj = {}; 304 | for (var x = 0; x < fields.length; x++) { // for all fields 305 | var field = fields[x]; // shallow copy 306 | if ('value' in field) { 307 | valueObj[field['name']] = field['value']; // creates key value pairs 308 | } else if (field['type'] === 'checkbox') { // if checkbox that has no value 309 | valueObj[field['name']] = false; // default false 310 | } 311 | 312 | } 313 | return valueObj; 314 | }; 315 | 316 | /** 317 | * Checks to see if all the fields have the valid flag set to true. returns false otherwise. 318 | * @param fields 319 | * @returns {boolean} 320 | */ 321 | this.checkValidFromFields = function (fields) { 322 | var valid = true; 323 | for (var x = 0; x < fields.length; x++) { // for all fields 324 | var field = fields[x]; // shallow copy 325 | if (!field['valid']) { // if false field exists 326 | valid = false; 327 | } 328 | } 329 | return valid; 330 | }; 331 | 332 | /** 333 | * Requirements check. This goes through and finds any fields marked as required and errors them out if 334 | * they have not been filled out. 335 | * @param fields 336 | * @returns {*} 337 | */ 338 | this.checkValidRequirements = function (fields) { 339 | for (var x = 0; x < fields.length; x++) { // for all fields 340 | var field = fields[x]; // shallow copy 341 | if ('required' in field) { // if the field is required 342 | if (field['required'] && field['valid'] && !('value' in field)) { // and it has no value and is still valid 343 | field['valid'] = false; // invalidate field 344 | field['errorMessage'] = 'This field is required'; 345 | } 346 | } 347 | } 348 | return fields; 349 | }; 350 | 351 | /** 352 | * Reset valid fields only for revalidation purposes 353 | * @param fields 354 | */ 355 | this.resetValidity = function (fields) { 356 | for (var x = 0; x < fields.length; x++) { // for all fields 357 | var field = fields[x]; // shallow copy 358 | field['valid'] = true; 359 | delete field['errorMessage']; 360 | } 361 | return fields; 362 | }; 363 | 364 | /** 365 | * Matches the names of those in values with those in fields adding values to fields. 366 | * @param values 367 | * @param fields 368 | */ 369 | this.addValuesToFields = function (values, fields) { 370 | for (var x = 0; x < fields.length; x++) { // for all fields 371 | var field = fields[x]; // shallow copy 372 | for (var key in values) { 373 | if (values.hasOwnProperty(key)) { // not sure why this needs to be set 374 | if (field['name'] === key) { 375 | //console.log('matched: ' + field['name']); 376 | field['value'] = values[key]; // set value accordingly 377 | } 378 | } 379 | } 380 | } 381 | return fields; 382 | }; 383 | 384 | }]); 385 | 386 | }()); -------------------------------------------------------------------------------- /anbomofo.min.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";var e=angular.module("autoModals",["ui.bootstrap","ui.router"]);e.directive("modal",function(){return{restrict:"A",scope:{ngService:"@",ngModel:"="},controller:["$scope","$attrs","$injector","$uibModal",function(e,i,t,l){e.dataServiceName=i.ngService,e.dataService=t.get(e.dataServiceName),e.openModal=function(){function i(){l.open({template:' ',controller:"AddModalInstanceController",size:"lg",resolve:{ngModel:function(){return e.ngModel},dataServiceName:function(){return e.dataServiceName}}})}function t(){setTimeout(function(){e.ready?i():(e.ready=e.dataService.modelReady(),t())},100)}"function"==typeof e.dataService.preClick&&e.dataService.preClick(),"function"==typeof e.dataService.modelReady?(e.ready=e.dataService.modelReady(),t()):i()}}],link:function(e,i,t,l){i.bind("click",function(i){e.openModal()})}}}),e.controller("AddModalInstanceController",["$scope","$uibModalInstance","$http","$state","$injector","dataServiceName","ngModel","ModalService",function(e,i,t,l,r,d,a,o){var n=r.get(d),s=n.getModel();e.fields=s.fields,void 0===a?e.settings=s.addModalSettings:(e.settings=s.editModalSettings,e.fields=o.addValuesToFields(a,e.fields)),e.title=e.settings.title,e.cancel=function(){i.dismiss("cancel")},e.ok=function(t){var l=o.getValuesFromFields(t),r=function(){e.fields=o.checkValidRequirements(e.fields),o.checkValidFromFields(e.fields)?(e.settings.hasOwnProperty("callback")&&e.settings.callback(l),i.close()):e.fields=t};"validate"in e.settings?(e.fields=o.resetValidity(t),e.fields=e.settings.validate(e.fields),r()):(e.fields=o.resetValidity(t),r())},i.result.then(function(i){e.fields=o.resetModel(n.getModel())},function(i){e.fields=o.resetModel(n.getModel())})}]),e.service("ModalService",[function(){this.compileModel=function(i){for(var t=!1,l=0;l" 7 | ], 8 | "description": "Angular bootstrap modal library for automatically creating and deploying forms in modals", 9 | "main": "anbomofo.js", 10 | "moduleType": [ 11 | "globals" 12 | ], 13 | "keywords": [ 14 | "AngularJS", 15 | "Bootstrap", 16 | "Forms", 17 | "Data", 18 | "Driven", 19 | "Forms", 20 | "Automatic", 21 | "form", 22 | "generation" 23 | ], 24 | "license": "MIT", 25 | "ignore": [ 26 | "**/.*", 27 | "node_modules", 28 | "bower_components", 29 | "test", 30 | "tests", 31 | "src", 32 | "readmeImages" 33 | ], 34 | "dependencies": { 35 | "angular-bootstrap": "^1.3.3", 36 | "angular-ui-router": "^0.3.1" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /bower_components/angular-bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "name": "https://github.com/angular-ui/bootstrap/graphs/contributors" 4 | }, 5 | "name": "angular-bootstrap", 6 | "keywords": [ 7 | "angular", 8 | "angular-ui", 9 | "bootstrap" 10 | ], 11 | "license": "MIT", 12 | "ignore": [], 13 | "description": "Native AngularJS (Angular) directives for Bootstrap.", 14 | "version": "1.3.3", 15 | "main": [ 16 | "./ui-bootstrap-tpls.js" 17 | ], 18 | "dependencies": { 19 | "angular": ">=1.4.0" 20 | }, 21 | "homepage": "https://github.com/angular-ui/bootstrap-bower", 22 | "_release": "1.3.3", 23 | "_resolution": { 24 | "type": "version", 25 | "tag": "1.3.3", 26 | "commit": "d45246707f5bf9533e3824861a29abd36757db45" 27 | }, 28 | "_source": "https://github.com/angular-ui/bootstrap-bower.git", 29 | "_target": "^1.3.3", 30 | "_originalSource": "angular-bootstrap", 31 | "_direct": true 32 | } -------------------------------------------------------------------------------- /bower_components/angular-bootstrap/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /bower_components/angular-bootstrap/.npmignore: -------------------------------------------------------------------------------- 1 | bower.json -------------------------------------------------------------------------------- /bower_components/angular-bootstrap/README.md: -------------------------------------------------------------------------------- 1 | ### UI Bootstrap - [AngularJS](http://angularjs.org/) directives specific to [Bootstrap](http://getbootstrap.com) 2 | 3 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular-ui/bootstrap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | [![Build Status](https://secure.travis-ci.org/angular-ui/bootstrap.svg)](http://travis-ci.org/angular-ui/bootstrap) 5 | [![devDependency Status](https://david-dm.org/angular-ui/bootstrap/dev-status.svg?branch=master)](https://david-dm.org/angular-ui/bootstrap#info=devDependencies) 6 | 7 | ### Quick links 8 | - [Demo](#demo) 9 | - [Installation](#installation) 10 | - [NPM](#install-with-npm) 11 | - [Bower](#install-with-bower) 12 | - [NuGet](#install-with-nuget) 13 | - [Custom](#custom-build) 14 | - [Manual](#manual-download) 15 | - [Support](#support) 16 | - [FAQ](#faq) 17 | - [Supported browsers](#supported-browsers) 18 | - [Need help?](#need-help) 19 | - [Found a bug?](#found-a-bug) 20 | - [Contributing to the project](#contributing-to-the-project) 21 | - [Development, meeting minutes, roadmap and more.](#development-meeting-minutes-roadmap-and-more) 22 | 23 | 24 | # Demo 25 | 26 | Do you want to see directives in action? Visit http://angular-ui.github.io/bootstrap/! 27 | 28 | # Installation 29 | 30 | Installation is easy as UI Bootstrap has minimal dependencies - only the AngularJS and Twitter Bootstrap's CSS are required. 31 | Note: Since version 0.13.0, UI Bootstrap depends on [ngAnimate](https://docs.angularjs.org/api/ngAnimate) for transitions and animations, such as the accordion, carousel, etc. Include `ngAnimate` in the module dependencies for your app in order to enable animation. 32 | 33 | #### Install with NPM 34 | 35 | ```sh 36 | $ npm install angular-ui-bootstrap 37 | ``` 38 | 39 | This will install AngularJS and Bootstrap NPM packages. 40 | 41 | #### Install with Bower 42 | ```sh 43 | $ bower install angular-bootstrap 44 | ``` 45 | 46 | Note: do not install 'angular-ui-bootstrap'. A separate repository - [bootstrap-bower](https://github.com/angular-ui/bootstrap-bower) - hosts the compiled javascript file and bower.json. 47 | 48 | #### Install with NuGet 49 | To install AngularJS UI Bootstrap, run the following command in the Package Manager Console 50 | 51 | ```sh 52 | PM> Install-Package Angular.UI.Bootstrap 53 | ``` 54 | 55 | #### Custom build 56 | 57 | Head over to http://angular-ui.github.io/bootstrap/ and hit the *Custom build* button to create your own custom UI Bootstrap build, just the way you like it. 58 | 59 | #### Manual download 60 | 61 | After downloading dependencies (or better yet, referencing them from your favorite CDN) you need to download build version of this project. All the files and their purposes are described here: 62 | https://github.com/angular-ui/bootstrap/tree/gh-pages#build-files 63 | Don't worry, if you are not sure which file to take, opt for `ui-bootstrap-tpls-[version].min.js`. 64 | 65 | ### Adding dependency to your project 66 | 67 | When you are done downloading all the dependencies and project files the only remaining part is to add dependencies on the `ui.bootstrap` AngularJS module: 68 | 69 | ```js 70 | angular.module('myModule', ['ui.bootstrap']); 71 | ``` 72 | 73 | If you're a Browserify or Webpack user, you can do: 74 | 75 | ```js 76 | var uibs = require('angular-ui-bootstrap'); 77 | 78 | angular.module('myModule', [uibs]); 79 | ``` 80 | 81 | # Support 82 | 83 | ## FAQ 84 | 85 | https://github.com/angular-ui/bootstrap/wiki/FAQ 86 | 87 | ## Supported browsers 88 | 89 | Directives from this repository are automatically tested with the following browsers: 90 | * Chrome (stable and canary channel) 91 | * Firefox 92 | * IE 9 and 10 93 | * Opera 94 | * Safari 95 | 96 | Modern mobile browsers should work without problems. 97 | 98 | 99 | ## Need help? 100 | Need help using UI Bootstrap? 101 | 102 | * Live help in the IRC (`#angularjs` channel at the `freenode` network). Use this [webchat](https://webchat.freenode.net/) or your own IRC client. 103 | * Ask a question in [StackOverflow](http://stackoverflow.com/) under the [angular-ui-bootstrap](http://stackoverflow.com/questions/tagged/angular-ui-bootstrap) tag. 104 | 105 | **Please do not create new issues in this repository to ask questions about using UI Bootstrap** 106 | 107 | ## Found a bug? 108 | Please take a look at [CONTRIBUTING.md](CONTRIBUTING.md#you-think-youve-found-a-bug) and submit your issue [here](https://github.com/angular-ui/bootstrap/issues/new). 109 | 110 | 111 | ---- 112 | 113 | 114 | # Contributing to the project 115 | 116 | We are always looking for the quality contributions! Please check the [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution guidelines. 117 | 118 | # Development, meeting minutes, roadmap and more. 119 | 120 | Head over to the [Wiki](https://github.com/angular-ui/bootstrap/wiki) for notes on development for UI Bootstrap, meeting minutes from the UI Bootstrap team, roadmap plans, project philosophy and more. 121 | -------------------------------------------------------------------------------- /bower_components/angular-bootstrap/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "name": "https://github.com/angular-ui/bootstrap/graphs/contributors" 4 | }, 5 | "name": "angular-bootstrap", 6 | "keywords": [ 7 | "angular", 8 | "angular-ui", 9 | "bootstrap" 10 | ], 11 | "license": "MIT", 12 | "ignore": [], 13 | "description": "Native AngularJS (Angular) directives for Bootstrap.", 14 | "version": "1.3.3", 15 | "main": ["./ui-bootstrap-tpls.js"], 16 | "dependencies": { 17 | "angular": ">=1.4.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /bower_components/angular-bootstrap/index.js: -------------------------------------------------------------------------------- 1 | require('./ui-bootstrap-tpls'); 2 | module.exports = 'ui.bootstrap'; 3 | -------------------------------------------------------------------------------- /bower_components/angular-bootstrap/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ui-bootstrap", 3 | "version": "1.3.3", 4 | "description": "Bootstrap widgets for Angular", 5 | "main": "index.js", 6 | "homepage": "http://angular-ui.github.io/bootstrap/", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/angular-ui/bootstrap.git" 10 | }, 11 | "keywords": [ 12 | "angular", 13 | "bootstrap", 14 | "angular-ui", 15 | "components", 16 | "client-side" 17 | ], 18 | "author": "https://github.com/angular-ui/bootstrap/graphs/contributors", 19 | "peerDependencies": { 20 | "angular": ">= 1.4.0-beta.0 || >= 1.5.0-beta.0" 21 | }, 22 | "license": "MIT" 23 | } 24 | -------------------------------------------------------------------------------- /bower_components/angular-bootstrap/ui-bootstrap-csp.css: -------------------------------------------------------------------------------- 1 | /* Include this file in your html if you are using the CSP mode. */ 2 | 3 | .ng-animate.item:not(.left):not(.right) { 4 | -webkit-transition: 0s ease-in-out left; 5 | transition: 0s ease-in-out left 6 | } 7 | .uib-datepicker .uib-title { 8 | width: 100%; 9 | } 10 | 11 | .uib-day button, .uib-month button, .uib-year button { 12 | min-width: 100%; 13 | } 14 | 15 | .uib-left, .uib-right { 16 | width: 100% 17 | } 18 | 19 | .uib-position-measure { 20 | display: block !important; 21 | visibility: hidden !important; 22 | position: absolute !important; 23 | top: -9999px !important; 24 | left: -9999px !important; 25 | } 26 | 27 | .uib-position-scrollbar-measure { 28 | position: absolute !important; 29 | top: -9999px !important; 30 | width: 50px !important; 31 | height: 50px !important; 32 | overflow: scroll !important; 33 | } 34 | 35 | .uib-position-body-scrollbar-measure { 36 | overflow: scroll !important; 37 | } 38 | .uib-datepicker-popup.dropdown-menu { 39 | display: block; 40 | float: none; 41 | margin: 0; 42 | } 43 | 44 | .uib-button-bar { 45 | padding: 10px 9px 2px; 46 | } 47 | 48 | [uib-tooltip-popup].tooltip.top-left > .tooltip-arrow, 49 | [uib-tooltip-popup].tooltip.top-right > .tooltip-arrow, 50 | [uib-tooltip-popup].tooltip.bottom-left > .tooltip-arrow, 51 | [uib-tooltip-popup].tooltip.bottom-right > .tooltip-arrow, 52 | [uib-tooltip-popup].tooltip.left-top > .tooltip-arrow, 53 | [uib-tooltip-popup].tooltip.left-bottom > .tooltip-arrow, 54 | [uib-tooltip-popup].tooltip.right-top > .tooltip-arrow, 55 | [uib-tooltip-popup].tooltip.right-bottom > .tooltip-arrow, 56 | [uib-tooltip-html-popup].tooltip.top-left > .tooltip-arrow, 57 | [uib-tooltip-html-popup].tooltip.top-right > .tooltip-arrow, 58 | [uib-tooltip-html-popup].tooltip.bottom-left > .tooltip-arrow, 59 | [uib-tooltip-html-popup].tooltip.bottom-right > .tooltip-arrow, 60 | [uib-tooltip-html-popup].tooltip.left-top > .tooltip-arrow, 61 | [uib-tooltip-html-popup].tooltip.left-bottom > .tooltip-arrow, 62 | [uib-tooltip-html-popup].tooltip.right-top > .tooltip-arrow, 63 | [uib-tooltip-html-popup].tooltip.right-bottom > .tooltip-arrow, 64 | [uib-tooltip-template-popup].tooltip.top-left > .tooltip-arrow, 65 | [uib-tooltip-template-popup].tooltip.top-right > .tooltip-arrow, 66 | [uib-tooltip-template-popup].tooltip.bottom-left > .tooltip-arrow, 67 | [uib-tooltip-template-popup].tooltip.bottom-right > .tooltip-arrow, 68 | [uib-tooltip-template-popup].tooltip.left-top > .tooltip-arrow, 69 | [uib-tooltip-template-popup].tooltip.left-bottom > .tooltip-arrow, 70 | [uib-tooltip-template-popup].tooltip.right-top > .tooltip-arrow, 71 | [uib-tooltip-template-popup].tooltip.right-bottom > .tooltip-arrow, 72 | [uib-popover-popup].popover.top-left > .arrow, 73 | [uib-popover-popup].popover.top-right > .arrow, 74 | [uib-popover-popup].popover.bottom-left > .arrow, 75 | [uib-popover-popup].popover.bottom-right > .arrow, 76 | [uib-popover-popup].popover.left-top > .arrow, 77 | [uib-popover-popup].popover.left-bottom > .arrow, 78 | [uib-popover-popup].popover.right-top > .arrow, 79 | [uib-popover-popup].popover.right-bottom > .arrow, 80 | [uib-popover-html-popup].popover.top-left > .arrow, 81 | [uib-popover-html-popup].popover.top-right > .arrow, 82 | [uib-popover-html-popup].popover.bottom-left > .arrow, 83 | [uib-popover-html-popup].popover.bottom-right > .arrow, 84 | [uib-popover-html-popup].popover.left-top > .arrow, 85 | [uib-popover-html-popup].popover.left-bottom > .arrow, 86 | [uib-popover-html-popup].popover.right-top > .arrow, 87 | [uib-popover-html-popup].popover.right-bottom > .arrow, 88 | [uib-popover-template-popup].popover.top-left > .arrow, 89 | [uib-popover-template-popup].popover.top-right > .arrow, 90 | [uib-popover-template-popup].popover.bottom-left > .arrow, 91 | [uib-popover-template-popup].popover.bottom-right > .arrow, 92 | [uib-popover-template-popup].popover.left-top > .arrow, 93 | [uib-popover-template-popup].popover.left-bottom > .arrow, 94 | [uib-popover-template-popup].popover.right-top > .arrow, 95 | [uib-popover-template-popup].popover.right-bottom > .arrow { 96 | top: auto; 97 | bottom: auto; 98 | left: auto; 99 | right: auto; 100 | margin: 0; 101 | } 102 | 103 | [uib-popover-popup].popover, 104 | [uib-popover-html-popup].popover, 105 | [uib-popover-template-popup].popover { 106 | display: block !important; 107 | } 108 | 109 | .uib-time input { 110 | width: 50px; 111 | } 112 | 113 | [uib-typeahead-popup].dropdown-menu { 114 | display: block; 115 | } 116 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ui-router", 3 | "version": "0.3.1", 4 | "license": "MIT", 5 | "main": "./release/angular-ui-router.js", 6 | "dependencies": { 7 | "angular": "^1.0.8" 8 | }, 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "component.json", 14 | "package.json", 15 | "lib", 16 | "config", 17 | "sample", 18 | "test", 19 | "tests", 20 | "ngdoc_assets", 21 | "Gruntfile.js", 22 | "files.js" 23 | ], 24 | "homepage": "https://github.com/angular-ui/angular-ui-router-bower", 25 | "_release": "0.3.1", 26 | "_resolution": { 27 | "type": "version", 28 | "tag": "0.3.1", 29 | "commit": "6ac1c61991121f5324f99089003314bba3bc6a95" 30 | }, 31 | "_source": "https://github.com/angular-ui/angular-ui-router-bower.git", 32 | "_target": "^0.3.1", 33 | "_originalSource": "angular-ui-router", 34 | "_direct": true 35 | } -------------------------------------------------------------------------------- /bower_components/angular-ui-router/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Report an Issue 3 | 4 | Help us make UI-Router better! If you think you might have found a bug, or some other weirdness, start by making sure 5 | it hasn't already been reported. You can [search through existing issues](https://github.com/angular-ui/ui-router/search?q=wat%3F&type=Issues) 6 | to see if someone's reported one similar to yours. 7 | 8 | If not, then [create a plunkr](http://bit.ly/UIR-Plunk) that demonstrates the problem (try to use as little code 9 | as possible: the more minimalist, the faster we can debug it). 10 | 11 | Next, [create a new issue](https://github.com/angular-ui/ui-router/issues/new) that briefly explains the problem, 12 | and provides a bit of background as to the circumstances that triggered it. Don't forget to include the link to 13 | that plunkr you created! 14 | 15 | **Note**: If you're unsure how a feature is used, or are encountering some unexpected behavior that you aren't sure 16 | is a bug, it's best to talk it out on 17 | [StackOverflow](http://stackoverflow.com/questions/ask?tags=angularjs,angular-ui-router) before reporting it. This 18 | keeps development streamlined, and helps us focus on building great software. 19 | 20 | 21 | Issues only! | 22 | -------------| 23 | Please keep in mind that the issue tracker is for *issues*. Please do *not* post an issue if you need help or support. Instead, see one of the above-mentioned forums or [IRC](irc://irc.freenode.net/#angularjs). | 24 | 25 | ####Purple Labels 26 | A purple label means that **you** need to take some further action. 27 | - ![Not Actionable - Need Info](ngdoc_assets/incomplete.png): Your issue is not specific enough, or there is no clear action that we can take. Please clarify and refine your issue. 28 | - ![Plunkr Please](ngdoc_assets/example.png): Please [create a plunkr](http://bit.ly/UIR-Plunk) 29 | - ![StackOverflow](ngdoc_assets/so.png): We suspect your issue is really a help request, or could be answered by the community. Please ask your question on [StackOverflow](http://stackoverflow.com/questions/ask?tags=angularjs,angular-ui-router). If you determine that is an actual issue, please explain why. 30 | 31 | If your issue gets labeled with purple label, no further action will be taken until you respond to the label appropriately. 32 | 33 | # Contribute 34 | 35 | **(1)** See the **[Developing](#developing)** section below, to get the development version of UI-Router up and running on your local machine. 36 | 37 | **(2)** Check out the [roadmap](https://github.com/angular-ui/ui-router/milestones) to see where the project is headed, and if your feature idea fits with where we're headed. 38 | 39 | **(3)** If you're not sure, [open an RFC](https://github.com/angular-ui/ui-router/issues/new?title=RFC:%20My%20idea) to get some feedback on your idea. 40 | 41 | **(4)** Finally, commit some code and open a pull request. Code & commits should abide by the following rules: 42 | 43 | - *Always* have test coverage for new features (or regression tests for bug fixes), and *never* break existing tests 44 | - Commits should represent one logical change each; if a feature goes through multiple iterations, squash your commits down to one 45 | - Make sure to follow the [Angular commit message format](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format) so your change will appear in the changelog of the next release. 46 | - Changes should always respect the coding style of the project 47 | 48 | 49 | 50 | # Developing 51 | 52 | UI-Router uses grunt >= 0.4.x. Make sure to upgrade your environment and read the 53 | [Migration Guide](http://gruntjs.com/upgrading-from-0.3-to-0.4). 54 | 55 | Dependencies for building from source and running tests: 56 | 57 | * [grunt-cli](https://github.com/gruntjs/grunt-cli) - run: `$ npm install -g grunt-cli` 58 | * Then, install the development dependencies by running `$ npm install` from the project directory 59 | 60 | There are a number of targets in the gruntfile that are used to generating different builds: 61 | 62 | * `grunt`: Perform a normal build, runs jshint and karma tests 63 | * `grunt build`: Perform a normal build 64 | * `grunt dist`: Perform a clean build and generate documentation 65 | * `grunt dev`: Run dev server (sample app) and watch for changes, builds and runs karma tests on changes. 66 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2013-2015 The AngularUI Team, Karsten Sperling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/README.md: -------------------------------------------------------------------------------- 1 | # AngularUI Router  [![Build Status](https://travis-ci.org/angular-ui/ui-router.svg?branch=master)](https://travis-ci.org/angular-ui/ui-router) 2 | 3 | #### The de-facto solution to flexible routing with nested views 4 | --- 5 | **[Download 0.2.18](https://cdn.rawgit.com/angular-ui/ui-router/0.2.18/release/angular-ui-router.js)** (or **[Minified](https://cdn.rawgit.com/angular-ui/ui-router/0.2.18/release/angular-ui-router.min.js)**) **|** 6 | **[Guide](https://github.com/angular-ui/ui-router/wiki) |** 7 | **[API](http://angular-ui.github.io/ui-router/site) |** 8 | **[Sample](http://angular-ui.github.com/ui-router/sample/) ([Src](https://github.com/angular-ui/ui-router/tree/gh-pages/sample)) |** 9 | **[FAQ](https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions) |** 10 | **[Resources](#resources) |** 11 | **[Report an Issue](https://github.com/angular-ui/ui-router/blob/master/CONTRIBUTING.md#report-an-issue) |** 12 | **[Contribute](https://github.com/angular-ui/ui-router/blob/master/CONTRIBUTING.md#contribute) |** 13 | **[Help!](http://stackoverflow.com/questions/ask?tags=angularjs,angular-ui-router) |** 14 | **[Discuss](https://groups.google.com/forum/#!categories/angular-ui/router)** 15 | 16 | --- 17 | 18 | *_Please help us out by testing the 1.0 alpha release!_* 19 | 20 | [1.0.0alpha0 Announcement](https://github.com/angular-ui/ui-router/releases/tag/1.0.0alpha0) ([Source Code](https://github.com/angular-ui/ui-router/tree/master)) | [Sample App](http://ui-router.github.io/sample-app/) ([Source Code](https://github.com/ui-router/sample-app)) | [Known Issues](https://github.com/angular-ui/ui-router/issues?q=is%3Aissue+is%3Aopen+label%3A1.0) 21 | 22 | 23 | --- 24 | 25 | AngularUI Router is a routing framework for [AngularJS](http://angularjs.org), which allows you to organize the 26 | parts of your interface into a [*state machine*](https://en.wikipedia.org/wiki/Finite-state_machine). Unlike the 27 | [`$route` service](http://docs.angularjs.org/api/ngRoute.$route) in the Angular ngRoute module, which is organized around URL 28 | routes, UI-Router is organized around [*states*](https://github.com/angular-ui/ui-router/wiki), 29 | which may optionally have routes, as well as other behavior, attached. 30 | 31 | States are bound to *named*, *nested* and *parallel views*, allowing you to powerfully manage your application's interface. 32 | 33 | Check out the sample app: http://angular-ui.github.io/ui-router/sample/ 34 | 35 | - 36 | **Note:** *UI-Router is under active development. As such, while this library is well-tested, the API may change. Consider using it in production applications only if you're comfortable following a changelog and updating your usage accordingly.* 37 | 38 | 39 | ## Get Started 40 | 41 | **(1)** Get UI-Router in one of the following ways: 42 | - clone & [build](CONTRIBUTING.md#developing) this repository 43 | - [download the release](http://angular-ui.github.io/ui-router/release/angular-ui-router.js) (or [minified](http://angular-ui.github.io/ui-router/release/angular-ui-router.min.js)) 44 | - [link to cdn](http://cdnjs.com/libraries/angular-ui-router) 45 | - via **[jspm](http://jspm.io/)**: by running `$ jspm install angular-ui-router` from your console 46 | - or via **[npm](https://www.npmjs.org/)**: by running `$ npm install angular-ui-router` from your console 47 | - or via **[Bower](http://bower.io/)**: by running `$ bower install angular-ui-router` from your console 48 | - or via **[Component](https://github.com/component/component)**: by running `$ component install angular-ui/ui-router` from your console 49 | 50 | **(2)** Include `angular-ui-router.js` (or `angular-ui-router.min.js`) in your `index.html`, after including Angular itself (For Component users: ignore this step) 51 | 52 | **(3)** Add `'ui.router'` to your main module's list of dependencies (For Component users: replace `'ui.router'` with `require('angular-ui-router')`) 53 | 54 | When you're done, your setup should look similar to the following: 55 | 56 | > 57 | ```html 58 | 59 | 60 | 61 | 62 | 63 | 68 | ... 69 | 70 | 71 | ... 72 | 73 | 74 | ``` 75 | 76 | ### [Nested States & Views](http://plnkr.co/edit/u18KQc?p=preview) 77 | 78 | The majority of UI-Router's power is in its ability to nest states & views. 79 | 80 | **(1)** First, follow the [setup](#get-started) instructions detailed above. 81 | 82 | **(2)** Then, add a [`ui-view` directive](https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-view) to the `` of your app. 83 | 84 | > 85 | ```html 86 | 87 | 88 |
89 | 90 | State 1 91 | State 2 92 | 93 | ``` 94 | 95 | **(3)** You'll notice we also added some links with [`ui-sref` directives](https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-sref). In addition to managing state transitions, this directive auto-generates the `href` attribute of the `` element it's attached to, if the corresponding state has a URL. Next we'll add some templates. These will plug into the `ui-view` within `index.html`. Notice that they have their own `ui-view` as well! That is the key to nesting states and views. 96 | 97 | > 98 | ```html 99 | 100 |

State 1

101 |
102 |
Show List 103 |
104 | ``` 105 | ```html 106 | 107 |

State 2

108 |
109 | Show List 110 |
111 | ``` 112 | 113 | **(4)** Next, we'll add some child templates. *These* will get plugged into the `ui-view` of their parent state templates. 114 | 115 | > 116 | ```html 117 | 118 |

List of State 1 Items

119 | 122 | ``` 123 | 124 | > 125 | ```html 126 | 127 |

List of State 2 Things

128 | 131 | ``` 132 | 133 | **(5)** Finally, we'll wire it all up with `$stateProvider`. Set up your states in the module config, as in the following: 134 | 135 | 136 | > 137 | ```javascript 138 | myApp.config(function($stateProvider, $urlRouterProvider) { 139 | // 140 | // For any unmatched url, redirect to /state1 141 | $urlRouterProvider.otherwise("/state1"); 142 | // 143 | // Now set up the states 144 | $stateProvider 145 | .state('state1', { 146 | url: "/state1", 147 | templateUrl: "partials/state1.html" 148 | }) 149 | .state('state1.list', { 150 | url: "/list", 151 | templateUrl: "partials/state1.list.html", 152 | controller: function($scope) { 153 | $scope.items = ["A", "List", "Of", "Items"]; 154 | } 155 | }) 156 | .state('state2', { 157 | url: "/state2", 158 | templateUrl: "partials/state2.html" 159 | }) 160 | .state('state2.list', { 161 | url: "/list", 162 | templateUrl: "partials/state2.list.html", 163 | controller: function($scope) { 164 | $scope.things = ["A", "Set", "Of", "Things"]; 165 | } 166 | }); 167 | }); 168 | ``` 169 | 170 | **(6)** See this quick start example in action. 171 | >**[Go to Quick Start Plunker for Nested States & Views](http://plnkr.co/edit/u18KQc?p=preview)** 172 | 173 | **(7)** This only scratches the surface 174 | >**[Dive Deeper!](https://github.com/angular-ui/ui-router/wiki)** 175 | 176 | 177 | ### [Multiple & Named Views](http://plnkr.co/edit/SDOcGS?p=preview) 178 | 179 | Another great feature is the ability to have multiple `ui-view`s view per template. 180 | 181 | **Pro Tip:** *While multiple parallel views are a powerful feature, you'll often be able to manage your 182 | interfaces more effectively by nesting your views, and pairing those views with nested states.* 183 | 184 | **(1)** Follow the [setup](#get-started) instructions detailed above. 185 | 186 | **(2)** Add one or more `ui-view` to your app, give them names. 187 | > 188 | ```html 189 | 190 | 191 |
192 |
193 | 194 | Route 1 195 | Route 2 196 | 197 | ``` 198 | 199 | **(3)** Set up your states in the module config: 200 | > 201 | ```javascript 202 | myApp.config(function($stateProvider) { 203 | $stateProvider 204 | .state('index', { 205 | url: "", 206 | views: { 207 | "viewA": { template: "index.viewA" }, 208 | "viewB": { template: "index.viewB" } 209 | } 210 | }) 211 | .state('route1', { 212 | url: "/route1", 213 | views: { 214 | "viewA": { template: "route1.viewA" }, 215 | "viewB": { template: "route1.viewB" } 216 | } 217 | }) 218 | .state('route2', { 219 | url: "/route2", 220 | views: { 221 | "viewA": { template: "route2.viewA" }, 222 | "viewB": { template: "route2.viewB" } 223 | } 224 | }) 225 | }); 226 | ``` 227 | 228 | **(4)** See this quick start example in action. 229 | >**[Go to Quick Start Plunker for Multiple & Named Views](http://plnkr.co/edit/SDOcGS?p=preview)** 230 | 231 | 232 | ## Resources 233 | 234 | * [In-Depth Guide](https://github.com/angular-ui/ui-router/wiki) 235 | * [API Reference](http://angular-ui.github.io/ui-router/site) 236 | * [Sample App](http://angular-ui.github.com/ui-router/sample/) ([Source](https://github.com/angular-ui/ui-router/tree/gh-pages/sample)) 237 | * [FAQ](https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions) 238 | * [Slides comparing ngRoute to ui-router](http://slid.es/timkindberg/ui-router#/) 239 | * [UI-Router Extras / Addons](http://christopherthielen.github.io/ui-router-extras/#/home) (@christopherthielen) 240 | 241 | ### Videos 242 | 243 | * [Introduction Video](https://egghead.io/lessons/angularjs-introduction-ui-router) (egghead.io) 244 | * [Tim Kindberg on Angular UI-Router](https://www.youtube.com/watch?v=lBqiZSemrqg) 245 | * [Activating States](https://egghead.io/lessons/angularjs-ui-router-activating-states) (egghead.io) 246 | * [Learn Angular.js using UI-Router](http://youtu.be/QETUuZ27N0w) (LearnCode.academy) 247 | 248 | 249 | 250 | ## Reporting issues and Contributing 251 | 252 | Please read our [Contributor guidelines](CONTRIBUTING.md) before reporting an issue or creating a pull request. 253 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ui-router", 3 | "version": "0.3.1", 4 | "license" : "MIT", 5 | "main": "./release/angular-ui-router.js", 6 | "dependencies": { 7 | "angular": "^1.0.8" 8 | }, 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "component.json", 14 | "package.json", 15 | "lib", 16 | "config", 17 | "sample", 18 | "test", 19 | "tests", 20 | "ngdoc_assets", 21 | "Gruntfile.js", 22 | "files.js" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/release/angular-ui-router.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * State-based routing for AngularJS 3 | * @version v0.3.1 4 | * @link http://angular-ui.github.com/ 5 | * @license MIT License, http://www.opensource.org/licenses/MIT 6 | */ 7 | "undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ui.router"),function(a,b,c){"use strict";function d(a,b){return R(new(R(function(){},{prototype:a})),b)}function e(a){return Q(arguments,function(b){b!==a&&Q(b,function(b,c){a.hasOwnProperty(c)||(a[c]=b)})}),a}function f(a,b){var c=[];for(var d in a.path){if(a.path[d]!==b.path[d])break;c.push(a.path[d])}return c}function g(a){if(Object.keys)return Object.keys(a);var b=[];return Q(a,function(a,c){b.push(c)}),b}function h(a,b){if(Array.prototype.indexOf)return a.indexOf(b,Number(arguments[2])||0);var c=a.length>>>0,d=Number(arguments[2])||0;for(d=0>d?Math.ceil(d):Math.floor(d),0>d&&(d+=c);c>d;d++)if(d in a&&a[d]===b)return d;return-1}function i(a,b,c,d){var e,i=f(c,d),j={},k=[];for(var l in i)if(i[l]&&i[l].params&&(e=g(i[l].params),e.length))for(var m in e)h(k,e[m])>=0||(k.push(e[m]),j[e[m]]=a[e[m]]);return R({},j,b)}function j(a,b,c){if(!c){c=[];for(var d in a)c.push(d)}for(var e=0;e "));if(s[c]=d,N(a))q.push(c,[function(){return b.get(a)}],j);else{var e=b.annotate(a);Q(e,function(a){a!==c&&i.hasOwnProperty(a)&&n(i[a],a)}),q.push(c,a,e)}r.pop(),s[c]=f}}function o(a){return O(a)&&a.then&&a.$$promises}if(!O(i))throw new Error("'invocables' must be an object");var p=g(i||{}),q=[],r=[],s={};return Q(i,n),i=r=s=null,function(d,f,g){function h(){--u||(v||e(t,f.$$values),r.$$values=t,r.$$promises=r.$$promises||!0,delete r.$$inheritedValues,n.resolve(t))}function i(a){r.$$failure=a,n.reject(a)}function j(c,e,f){function j(a){l.reject(a),i(a)}function k(){if(!L(r.$$failure))try{l.resolve(b.invoke(e,g,t)),l.promise.then(function(a){t[c]=a,h()},j)}catch(a){j(a)}}var l=a.defer(),m=0;Q(f,function(a){s.hasOwnProperty(a)&&!d.hasOwnProperty(a)&&(m++,s[a].then(function(b){t[a]=b,--m||k()},j))}),m||k(),s[c]=l.promise}if(o(d)&&g===c&&(g=f,f=d,d=null),d){if(!O(d))throw new Error("'locals' must be an object")}else d=k;if(f){if(!o(f))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else f=l;var n=a.defer(),r=n.promise,s=r.$$promises={},t=R({},d),u=1+q.length/3,v=!1;if(L(f.$$failure))return i(f.$$failure),r;f.$$inheritedValues&&e(t,m(f.$$inheritedValues,p)),R(s,f.$$promises),f.$$values?(v=e(t,m(f.$$values,p)),r.$$inheritedValues=m(f.$$values,p),h()):(f.$$inheritedValues&&(r.$$inheritedValues=m(f.$$inheritedValues,p)),f.then(h,i));for(var w=0,x=q.length;x>w;w+=3)d.hasOwnProperty(q[w])?h():j(q[w],q[w+1],q[w+2]);return r}},this.resolve=function(a,b,c,d){return this.study(a)(b,c,d)}}function q(a,b,c){this.fromConfig=function(a,b,c){return L(a.template)?this.fromString(a.template,b):L(a.templateUrl)?this.fromUrl(a.templateUrl,b):L(a.templateProvider)?this.fromProvider(a.templateProvider,b,c):null},this.fromString=function(a,b){return M(a)?a(b):a},this.fromUrl=function(c,d){return M(c)&&(c=c(d)),null==c?null:a.get(c,{cache:b,headers:{Accept:"text/html"}}).then(function(a){return a.data})},this.fromProvider=function(a,b,d){return c.invoke(a,null,d||{params:b})}}function r(a,b,e){function f(b,c,d,e){if(q.push(b),o[b])return o[b];if(!/^\w+([-.]+\w+)*(?:\[\])?$/.test(b))throw new Error("Invalid parameter name '"+b+"' in pattern '"+a+"'");if(p[b])throw new Error("Duplicate parameter name '"+b+"' in pattern '"+a+"'");return p[b]=new U.Param(b,c,d,e),p[b]}function g(a,b,c,d){var e=["",""],f=a.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&");if(!b)return f;switch(c){case!1:e=["(",")"+(d?"?":"")];break;case!0:f=f.replace(/\/$/,""),e=["(?:/(",")|/)?"];break;default:e=["("+c+"|",")?"]}return f+e[0]+b+e[1]}function h(e,f){var g,h,i,j,k;return g=e[2]||e[3],k=b.params[g],i=a.substring(m,e.index),h=f?e[4]:e[4]||("*"==e[1]?".*":null),h&&(j=U.type(h)||d(U.type("string"),{pattern:new RegExp(h,b.caseInsensitive?"i":c)})),{id:g,regexp:h,segment:i,type:j,cfg:k}}b=R({params:{}},O(b)?b:{});var i,j=/([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,k=/([:]?)([\w\[\].-]+)|\{([\w\[\].-]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,l="^",m=0,n=this.segments=[],o=e?e.params:{},p=this.params=e?e.params.$$new():new U.ParamSet,q=[];this.source=a;for(var r,s,t;(i=j.exec(a))&&(r=h(i,!1),!(r.segment.indexOf("?")>=0));)s=f(r.id,r.type,r.cfg,"path"),l+=g(r.segment,s.type.pattern.source,s.squash,s.isOptional),n.push(r.segment),m=j.lastIndex;t=a.substring(m);var u=t.indexOf("?");if(u>=0){var v=this.sourceSearch=t.substring(u);if(t=t.substring(0,u),this.sourcePath=a.substring(0,m+u),v.length>0)for(m=0;i=k.exec(v);)r=h(i,!0),s=f(r.id,r.type,r.cfg,"search"),m=j.lastIndex}else this.sourcePath=a,this.sourceSearch="";l+=g(t)+(b.strict===!1?"/?":"")+"$",n.push(t),this.regexp=new RegExp(l,b.caseInsensitive?"i":c),this.prefix=n[0],this.$$paramNames=q}function s(a){R(this,a)}function t(){function a(a){return null!=a?a.toString().replace(/~/g,"~~").replace(/\//g,"~2F"):a}function e(a){return null!=a?a.toString().replace(/~2F/g,"/").replace(/~~/g,"~"):a}function f(){return{strict:p,caseInsensitive:m}}function i(a){return M(a)||P(a)&&M(a[a.length-1])}function j(){for(;w.length;){var a=w.shift();if(a.pattern)throw new Error("You cannot override a type's .pattern at runtime.");b.extend(u[a.name],l.invoke(a.def))}}function k(a){R(this,a||{})}U=this;var l,m=!1,p=!0,q=!1,u={},v=!0,w=[],x={string:{encode:a,decode:e,is:function(a){return null==a||!L(a)||"string"==typeof a},pattern:/[^\/]*/},"int":{encode:a,decode:function(a){return parseInt(a,10)},is:function(a){return L(a)&&this.decode(a.toString())===a},pattern:/\d+/},bool:{encode:function(a){return a?1:0},decode:function(a){return 0!==parseInt(a,10)},is:function(a){return a===!0||a===!1},pattern:/0|1/},date:{encode:function(a){return this.is(a)?[a.getFullYear(),("0"+(a.getMonth()+1)).slice(-2),("0"+a.getDate()).slice(-2)].join("-"):c},decode:function(a){if(this.is(a))return a;var b=this.capture.exec(a);return b?new Date(b[1],b[2]-1,b[3]):c},is:function(a){return a instanceof Date&&!isNaN(a.valueOf())},equals:function(a,b){return this.is(a)&&this.is(b)&&a.toISOString()===b.toISOString()},pattern:/[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,capture:/([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/},json:{encode:b.toJson,decode:b.fromJson,is:b.isObject,equals:b.equals,pattern:/[^\/]*/},any:{encode:b.identity,decode:b.identity,equals:b.equals,pattern:/.*/}};t.$$getDefaultValue=function(a){if(!i(a.value))return a.value;if(!l)throw new Error("Injectable functions cannot be called at configuration time");return l.invoke(a.value)},this.caseInsensitive=function(a){return L(a)&&(m=a),m},this.strictMode=function(a){return L(a)&&(p=a),p},this.defaultSquashPolicy=function(a){if(!L(a))return q;if(a!==!0&&a!==!1&&!N(a))throw new Error("Invalid squash policy: "+a+". Valid policies: false, true, arbitrary-string");return q=a,a},this.compile=function(a,b){return new r(a,R(f(),b))},this.isMatcher=function(a){if(!O(a))return!1;var b=!0;return Q(r.prototype,function(c,d){M(c)&&(b=b&&L(a[d])&&M(a[d]))}),b},this.type=function(a,b,c){if(!L(b))return u[a];if(u.hasOwnProperty(a))throw new Error("A type named '"+a+"' has already been defined.");return u[a]=new s(R({name:a},b)),c&&(w.push({name:a,def:c}),v||j()),this},Q(x,function(a,b){u[b]=new s(R({name:b},a))}),u=d(u,{}),this.$get=["$injector",function(a){return l=a,v=!1,j(),Q(x,function(a,b){u[b]||(u[b]=new s(a))}),this}],this.Param=function(a,d,e,f){function j(a){var b=O(a)?g(a):[],c=-1===h(b,"value")&&-1===h(b,"type")&&-1===h(b,"squash")&&-1===h(b,"array");return c&&(a={value:a}),a.$$fn=i(a.value)?a.value:function(){return a.value},a}function k(c,d,e){if(c.type&&d)throw new Error("Param '"+a+"' has two type configurations.");return d?d:c.type?b.isString(c.type)?u[c.type]:c.type instanceof s?c.type:new s(c.type):"config"===e?u.any:u.string}function m(){var b={array:"search"===f?"auto":!1},c=a.match(/\[\]$/)?{array:!0}:{};return R(b,c,e).array}function p(a,b){var c=a.squash;if(!b||c===!1)return!1;if(!L(c)||null==c)return q;if(c===!0||N(c))return c;throw new Error("Invalid squash policy: '"+c+"'. Valid policies: false, true, or arbitrary string")}function r(a,b,d,e){var f,g,i=[{from:"",to:d||b?c:""},{from:null,to:d||b?c:""}];return f=P(a.replace)?a.replace:[],N(e)&&f.push({from:e,to:c}),g=o(f,function(a){return a.from}),n(i,function(a){return-1===h(g,a.from)}).concat(f)}function t(){if(!l)throw new Error("Injectable functions cannot be called at configuration time");var a=l.invoke(e.$$fn);if(null!==a&&a!==c&&!x.type.is(a))throw new Error("Default value ("+a+") for parameter '"+x.id+"' is not an instance of Type ("+x.type.name+")");return a}function v(a){function b(a){return function(b){return b.from===a}}function c(a){var c=o(n(x.replace,b(a)),function(a){return a.to});return c.length?c[0]:a}return a=c(a),L(a)?x.type.$normalize(a):t()}function w(){return"{Param:"+a+" "+d+" squash: '"+A+"' optional: "+z+"}"}var x=this;e=j(e),d=k(e,d,f);var y=m();d=y?d.$asArray(y,"search"===f):d,"string"!==d.name||y||"path"!==f||e.value!==c||(e.value="");var z=e.value!==c,A=p(e,z),B=r(e,y,z,A);R(this,{id:a,type:d,location:f,array:y,squash:A,replace:B,isOptional:z,value:v,dynamic:c,config:e,toString:w})},k.prototype={$$new:function(){return d(this,R(new k,{$$parent:this}))},$$keys:function(){for(var a=[],b=[],c=this,d=g(k.prototype);c;)b.push(c),c=c.$$parent;return b.reverse(),Q(b,function(b){Q(g(b),function(b){-1===h(a,b)&&-1===h(d,b)&&a.push(b)})}),a},$$values:function(a){var b={},c=this;return Q(c.$$keys(),function(d){b[d]=c[d].value(a&&a[d])}),b},$$equals:function(a,b){var c=!0,d=this;return Q(d.$$keys(),function(e){var f=a&&a[e],g=b&&b[e];d[e].type.equals(f,g)||(c=!1)}),c},$$validates:function(a){var d,e,f,g,h,i=this.$$keys();for(d=0;de;e++)if(b(j[e]))return;k&&b(k)}}function o(){return i=i||e.$on("$locationChangeSuccess",n)}var p,q=g.baseHref(),r=d.url();return l||o(),{sync:function(){n()},listen:function(){return o()},update:function(a){return a?void(r=d.url()):void(d.url()!==r&&(d.url(r),d.replace()))},push:function(a,b,e){var f=a.format(b||{});null!==f&&b&&b["#"]&&(f+="#"+b["#"]),d.url(f),p=e&&e.$$avoidResync?d.url():c,e&&e.replace&&d.replace()},href:function(c,e,f){if(!c.validates(e))return null;var g=a.html5Mode();b.isObject(g)&&(g=g.enabled),g=g&&h.history;var i=c.format(e);if(f=f||{},g||null===i||(i="#"+a.hashPrefix()+i),null!==i&&e&&e["#"]&&(i+="#"+e["#"]),i=m(i,g,f.absolute),!f.absolute||!i)return i;var j=!g&&i?"/":"",k=d.port();return k=80===k||443===k?"":":"+k,[d.protocol(),"://",d.host(),k,j,i].join("")}}}var i,j=[],k=null,l=!1;this.rule=function(a){if(!M(a))throw new Error("'rule' must be a function");return j.push(a),this},this.otherwise=function(a){if(N(a)){var b=a;a=function(){return b}}else if(!M(a))throw new Error("'rule' must be a function");return k=a,this},this.when=function(a,b){var c,h=N(b);if(N(a)&&(a=d.compile(a)),!h&&!M(b)&&!P(b))throw new Error("invalid 'handler' in when()");var i={matcher:function(a,b){return h&&(c=d.compile(b),b=["$match",function(a){return c.format(a)}]),R(function(c,d){return g(c,b,a.exec(d.path(),d.search()))},{prefix:N(a.prefix)?a.prefix:""})},regex:function(a,b){if(a.global||a.sticky)throw new Error("when() RegExp must not be global or sticky");return h&&(c=b,b=["$match",function(a){return f(c,a)}]),R(function(c,d){return g(c,b,a.exec(d.path()))},{prefix:e(a)})}},j={matcher:d.isMatcher(a),regex:a instanceof RegExp};for(var k in j)if(j[k])return this.rule(i[k](a,b));throw new Error("invalid 'what' in when()")},this.deferIntercept=function(a){a===c&&(a=!0),l=a},this.$get=h,h.$inject=["$location","$rootScope","$injector","$browser","$sniffer"]}function v(a,e){function f(a){return 0===a.indexOf(".")||0===a.indexOf("^")}function m(a,b){if(!a)return c;var d=N(a),e=d?a:a.name,g=f(e);if(g){if(!b)throw new Error("No reference point given for path '"+e+"'");b=m(b);for(var h=e.split("."),i=0,j=h.length,k=b;j>i;i++)if(""!==h[i]||0!==i){if("^"!==h[i])break;if(!k.parent)throw new Error("Path '"+e+"' not valid for state '"+b.name+"'");k=k.parent}else k=b;h=h.slice(i).join("."),e=k.name+(k.name&&h?".":"")+h}var l=z[e];return!l||!d&&(d||l!==a&&l.self!==a)?c:l}function n(a,b){A[a]||(A[a]=[]),A[a].push(b)}function p(a){for(var b=A[a]||[];b.length;)q(b.shift())}function q(b){b=d(b,{self:b,resolve:b.resolve||{},toString:function(){return this.name}});var c=b.name;if(!N(c)||c.indexOf("@")>=0)throw new Error("State must have a valid name");if(z.hasOwnProperty(c))throw new Error("State '"+c+"' is already defined");var e=-1!==c.indexOf(".")?c.substring(0,c.lastIndexOf(".")):N(b.parent)?b.parent:O(b.parent)&&N(b.parent.name)?b.parent.name:"";if(e&&!z[e])return n(e,b.self);for(var f in C)M(C[f])&&(b[f]=C[f](b,C.$delegates[f]));return z[c]=b,!b[B]&&b.url&&a.when(b.url,["$match","$stateParams",function(a,c){y.$current.navigable==b&&j(a,c)||y.transitionTo(b,a,{inherit:!0,location:!1})}]),p(c),b}function r(a){return a.indexOf("*")>-1}function s(a){for(var b=a.split("."),c=y.$current.name.split("."),d=0,e=b.length;e>d;d++)"*"===b[d]&&(c[d]="*");return"**"===b[0]&&(c=c.slice(h(c,b[1])),c.unshift("**")),"**"===b[b.length-1]&&(c.splice(h(c,b[b.length-2])+1,Number.MAX_VALUE),c.push("**")),b.length!=c.length?!1:c.join("")===b.join("")}function t(a,b){return N(a)&&!L(b)?C[a]:M(b)&&N(a)?(C[a]&&!C.$delegates[a]&&(C.$delegates[a]=C[a]),C[a]=b,this):this}function u(a,b){return O(a)?b=a:b.name=a,q(b),this}function v(a,e,f,h,l,n,p,q,t){function u(b,c,d,f){var g=a.$broadcast("$stateNotFound",b,c,d);if(g.defaultPrevented)return p.update(),D;if(!g.retry)return null;if(f.$retry)return p.update(),E;var h=y.transition=e.when(g.retry);return h.then(function(){return h!==y.transition?A:(b.options.$retry=!0,y.transitionTo(b.to,b.toParams,b.options))},function(){return D}),p.update(),h}function v(a,c,d,g,i,j){function m(){var c=[];return Q(a.views,function(d,e){var g=d.resolve&&d.resolve!==a.resolve?d.resolve:{};g.$template=[function(){return f.load(e,{view:d,locals:i.globals,params:n,notify:j.notify})||""}],c.push(l.resolve(g,i.globals,i.resolve,a).then(function(c){if(M(d.controllerProvider)||P(d.controllerProvider)){var f=b.extend({},g,i.globals);c.$$controller=h.invoke(d.controllerProvider,null,f)}else c.$$controller=d.controller;c.$$state=a,c.$$controllerAs=d.controllerAs,c.$$resolveAs=d.resolveAs,i[e]=c}))}),e.all(c).then(function(){return i.globals})}var n=d?c:k(a.params.$$keys(),c),o={$stateParams:n};i.resolve=l.resolve(a.resolve,o,i.resolve,a);var p=[i.resolve.then(function(a){i.globals=a})];return g&&p.push(g),e.all(p).then(m).then(function(a){return i})}var A=e.reject(new Error("transition superseded")),C=e.reject(new Error("transition prevented")),D=e.reject(new Error("transition aborted")),E=e.reject(new Error("transition failed"));return x.locals={resolve:null,globals:{$stateParams:{}}},y={params:{},current:x.self,$current:x,transition:null},y.reload=function(a){return y.transitionTo(y.current,n,{reload:a||!0,inherit:!1,notify:!0})},y.go=function(a,b,c){return y.transitionTo(a,b,R({inherit:!0,relative:y.$current},c))},y.transitionTo=function(b,c,f){c=c||{},f=R({location:!0,inherit:!1,relative:null,notify:!0,reload:!1,$retry:!1},f||{});var g,j=y.$current,l=y.params,o=j.path,q=m(b,f.relative),r=c["#"];if(!L(q)){var s={to:b,toParams:c,options:f},t=u(s,j.self,l,f);if(t)return t;if(b=s.to,c=s.toParams,f=s.options,q=m(b,f.relative),!L(q)){if(!f.relative)throw new Error("No such state '"+b+"'");throw new Error("Could not resolve '"+b+"' from state '"+f.relative+"'")}}if(q[B])throw new Error("Cannot transition to abstract state '"+b+"'");if(f.inherit&&(c=i(n,c||{},y.$current,q)),!q.params.$$validates(c))return E;c=q.params.$$values(c),b=q;var z=b.path,D=0,F=z[D],G=x.locals,H=[];if(f.reload){if(N(f.reload)||O(f.reload)){if(O(f.reload)&&!f.reload.name)throw new Error("Invalid reload state object");var I=f.reload===!0?o[0]:m(f.reload);if(f.reload&&!I)throw new Error("No such reload state '"+(N(f.reload)?f.reload:f.reload.name)+"'");for(;F&&F===o[D]&&F!==I;)G=H[D]=F.locals,D++,F=z[D]}}else for(;F&&F===o[D]&&F.ownParams.$$equals(c,l);)G=H[D]=F.locals,D++,F=z[D];if(w(b,c,j,l,G,f))return r&&(c["#"]=r),y.params=c,S(y.params,n),S(k(b.params.$$keys(),n),b.locals.globals.$stateParams),f.location&&b.navigable&&b.navigable.url&&(p.push(b.navigable.url,c,{$$avoidResync:!0,replace:"replace"===f.location}),p.update(!0)),y.transition=null,e.when(y.current);if(c=k(b.params.$$keys(),c||{}),r&&(c["#"]=r),f.notify&&a.$broadcast("$stateChangeStart",b.self,c,j.self,l,f).defaultPrevented)return a.$broadcast("$stateChangeCancel",b.self,c,j.self,l),null==y.transition&&p.update(),C;for(var J=e.when(G),K=D;K=D;d--)g=o[d],g.self.onExit&&h.invoke(g.self.onExit,g.self,g.locals.globals),g.locals=null;for(d=D;d2?k.enter(a,null,c).then(d):k.enter(a,null,c,d)},leave:function(a,c){b.version.minor>2?k.leave(a).then(c):k.leave(a,c)}};if(j){var e=j&&j(c,a);return{enter:function(a,b,c){e.enter(a,null,b),c()},leave:function(a,b){e.leave(a),b()}}}return d()}var i=g(),j=i("$animator"),k=i("$animate"),l={restrict:"ECA",terminal:!0,priority:400,transclude:"element",compile:function(c,g,i){return function(c,g,j){function k(){if(m&&(m.remove(),m=null),o&&(o.$destroy(),o=null),n){var a=n.data("$uiViewAnim");s.leave(n,function(){a.$$animLeave.resolve(),m=null}),m=n,n=null}}function l(h){var l,m=A(c,j,g,e),t=m&&a.$current&&a.$current.locals[m];if(h||t!==p){l=c.$new(),p=a.$current.locals[m],l.$emit("$viewContentLoading",m);var u=i(l,function(a){var e=f.defer(),h=f.defer(),i={$animEnter:e.promise,$animLeave:h.promise,$$animLeave:h};a.data("$uiViewAnim",i),s.enter(a,g,function(){e.resolve(),o&&o.$emit("$viewContentAnimationEnded"),(b.isDefined(r)&&!r||c.$eval(r))&&d(a)}),k()});n=u,o=l,o.$emit("$viewContentLoaded",m),o.$eval(q)}}var m,n,o,p,q=j.onload||"",r=j.autoscroll,s=h(j,c);g.inheritedData("$uiView");c.$on("$stateChangeSuccess",function(){l(!1)}),l(!0)}}};return l}function z(a,c,d,e){return{restrict:"ECA",priority:-400,compile:function(f){var g=f.html();return function(f,h,i){var j=d.$current,k=A(f,i,h,e),l=j&&j.locals[k];if(l){h.data("$uiView",{name:k,state:l.$$state}),h.html(l.$template?l.$template:g);var m=b.extend({},l);f[l.$$resolveAs]=m;var n=a(h.contents());if(l.$$controller){l.$scope=f,l.$element=h;var o=c(l.$$controller,l);l.$$controllerAs&&(f[l.$$controllerAs]=o,f[l.$$controllerAs][l.$$resolveAs]=m),M(o.$onInit)&&o.$onInit(),h.data("$ngControllerController",o),h.children().data("$ngControllerController",o)}n(f)}}}}}function A(a,b,c,d){var e=d(b.uiView||b.name||"")(a),f=c.inheritedData("$uiView");return e.indexOf("@")>=0?e:e+"@"+(f?f.state.name:"")}function B(a,b){var c,d=a.match(/^\s*({[^}]*})\s*$/);if(d&&(a=b+"("+d[1]+")"),c=a.replace(/\n/g," ").match(/^([^(]+?)\s*(\((.*)\))?$/),!c||4!==c.length)throw new Error("Invalid state ref '"+a+"'");return{state:c[1],paramExpr:c[3]||null}}function C(a){var b=a.parent().inheritedData("$uiView");return b&&b.state&&b.state.name?b.state:void 0}function D(a){var b="[object SVGAnimatedString]"===Object.prototype.toString.call(a.prop("href")),c="FORM"===a[0].nodeName;return{attr:c?"action":b?"xlink:href":"href",isAnchor:"A"===a.prop("tagName").toUpperCase(),clickable:!c}}function E(a,b,c,d,e){return function(f){var g=f.which||f.button,h=e();if(!(g>1||f.ctrlKey||f.metaKey||f.shiftKey||a.attr("target"))){var i=c(function(){b.go(h.state,h.params,h.options)});f.preventDefault();var j=d.isAnchor&&!h.href?1:0;f.preventDefault=function(){j--<=0&&c.cancel(i)}}}}function F(a,b){return{relative:C(a)||b.$current,inherit:!0}}function G(a,c){return{restrict:"A",require:["?^uiSrefActive","?^uiSrefActiveEq"],link:function(d,e,f,g){var h,i=B(f.uiSref,a.current.name),j={state:i.state,href:null,params:null},k=D(e),l=g[1]||g[0],m=null;j.options=R(F(e,a),f.uiSrefOpts?d.$eval(f.uiSrefOpts):{});var n=function(c){c&&(j.params=b.copy(c)),j.href=a.href(i.state,j.params,j.options),m&&m(),l&&(m=l.$$addStateInfo(i.state,j.params)),null!==j.href&&f.$set(k.attr,j.href)};i.paramExpr&&(d.$watch(i.paramExpr,function(a){a!==j.params&&n(a)},!0),j.params=b.copy(d.$eval(i.paramExpr))),n(),k.clickable&&(h=E(e,a,c,k,function(){return j}),e.bind("click",h),d.$on("$destroy",function(){e.unbind("click",h)}))}}}function H(a,b){return{restrict:"A",require:["?^uiSrefActive","?^uiSrefActiveEq"],link:function(c,d,e,f){function g(b){m.state=b[0],m.params=b[1],m.options=b[2],m.href=a.href(m.state,m.params,m.options),n&&n(),j&&(n=j.$$addStateInfo(m.state,m.params)),m.href&&e.$set(i.attr,m.href)}var h,i=D(d),j=f[1]||f[0],k=[e.uiState,e.uiStateParams||null,e.uiStateOpts||null],l="["+k.map(function(a){return a||"null"}).join(", ")+"]",m={state:null,params:null,options:null,href:null},n=null;c.$watch(l,g,!0),g(c.$eval(l)),i.clickable&&(h=E(d,a,b,i,function(){return m}),d.bind("click",h),c.$on("$destroy",function(){d.unbind("click",h)}))}}}function I(a,b,c){return{restrict:"A",controller:["$scope","$element","$attrs","$timeout",function(b,d,e,f){function g(b,c,e){var f=a.get(b,C(d)),g=h(b,c),i={state:f||{name:b},params:c,hash:g};return p.push(i),q[g]=e,function(){var a=p.indexOf(i);-1!==a&&p.splice(a,1)}}function h(a,c){if(!N(a))throw new Error("state should be a string");return O(c)?a+T(c):(c=b.$eval(c),O(c)?a+T(c):a)}function i(){for(var a=0;a0)){var c=g(a,b,o);return i(),c}},b.$on("$stateChangeSuccess",i),i()}]}}function J(a){var b=function(b,c){return a.is(b,c)};return b.$stateful=!0,b}function K(a){var b=function(b,c,d){return a.includes(b,c,d)};return b.$stateful=!0,b}var L=b.isDefined,M=b.isFunction,N=b.isString,O=b.isObject,P=b.isArray,Q=b.forEach,R=b.extend,S=b.copy,T=b.toJson;b.module("ui.router.util",["ng"]),b.module("ui.router.router",["ui.router.util"]),b.module("ui.router.state",["ui.router.router","ui.router.util"]),b.module("ui.router",["ui.router.state"]),b.module("ui.router.compat",["ui.router"]),p.$inject=["$q","$injector"],b.module("ui.router.util").service("$resolve",p),q.$inject=["$http","$templateCache","$injector"],b.module("ui.router.util").service("$templateFactory",q);var U;r.prototype.concat=function(a,b){var c={caseInsensitive:U.caseInsensitive(),strict:U.strictMode(),squash:U.defaultSquashPolicy()};return new r(this.sourcePath+a+this.sourceSearch,R(c,b),this)},r.prototype.toString=function(){return this.source},r.prototype.exec=function(a,b){function c(a){function b(a){return a.split("").reverse().join("")}function c(a){return a.replace(/\\-/g,"-")}var d=b(a).split(/-(?!\\)/),e=o(d,b);return o(e,c).reverse()}var d=this.regexp.exec(a);if(!d)return null;b=b||{};var e,f,g,h=this.parameters(),i=h.length,j=this.segments.length-1,k={};if(j!==d.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");var l,m;for(e=0;j>e;e++){for(g=h[e],l=this.params[g],m=d[e+1],f=0;fe;e++){for(g=h[e],k[g]=this.params[g].value(b[g]),l=this.params[g],m=b[g],f=0;ff;f++){var k=h>f,l=d[f],m=e[l],n=m.value(a[l]),p=m.isOptional&&m.type.equals(m.value(),n),q=p?m.squash:!1,r=m.type.encode(n);if(k){var s=c[f+1],t=f+1===h;if(q===!1)null!=r&&(j+=P(r)?o(r,b).join("-"):encodeURIComponent(r)),j+=s;else if(q===!0){var u=j.match(/\/$/)?/\/?(.*)/:/(.*)/;j+=s.match(u)[1]}else N(q)&&(j+=q+s);t&&m.squash===!0&&"/"===j.slice(-1)&&(j=j.slice(0,-1))}else{if(null==r||p&&q!==!1)continue;if(P(r)||(r=[r]),0===r.length)continue;r=o(r,encodeURIComponent).join("&"+l+"="),j+=(g?"&":"?")+(l+"="+r),g=!0}}return j},s.prototype.is=function(a,b){return!0},s.prototype.encode=function(a,b){return a},s.prototype.decode=function(a,b){return a},s.prototype.equals=function(a,b){return a==b},s.prototype.$subPattern=function(){var a=this.pattern.toString();return a.substr(1,a.length-2)},s.prototype.pattern=/.*/,s.prototype.toString=function(){return"{Type:"+this.name+"}"},s.prototype.$normalize=function(a){return this.is(a)?a:this.decode(a)},s.prototype.$asArray=function(a,b){function d(a,b){function d(a,b){return function(){return a[b].apply(a,arguments)}}function e(a){return P(a)?a:L(a)?[a]:[]}function f(a){switch(a.length){case 0:return c;case 1:return"auto"===b?a[0]:a;default:return a}}function g(a){return!a}function h(a,b){return function(c){if(P(c)&&0===c.length)return c;c=e(c);var d=o(c,a);return b===!0?0===n(d,g).length:f(d)}}function i(a){return function(b,c){var d=e(b),f=e(c);if(d.length!==f.length)return!1;for(var g=0;g>> 0, from = Number(arguments[2]) || 0; 77 | from = (from < 0) ? Math.ceil(from) : Math.floor(from); 78 | 79 | if (from < 0) from += len; 80 | 81 | for (; from < len; from++) { 82 | if (from in array && array[from] === value) return from; 83 | } 84 | return -1; 85 | } 86 | 87 | /** 88 | * Merges a set of parameters with all parameters inherited between the common parents of the 89 | * current state and a given destination state. 90 | * 91 | * @param {Object} currentParams The value of the current state parameters ($stateParams). 92 | * @param {Object} newParams The set of parameters which will be composited with inherited params. 93 | * @param {Object} $current Internal definition of object representing the current state. 94 | * @param {Object} $to Internal definition of object representing state to transition to. 95 | */ 96 | function inheritParams(currentParams, newParams, $current, $to) { 97 | var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = []; 98 | 99 | for (var i in parents) { 100 | if (!parents[i] || !parents[i].params) continue; 101 | parentParams = objectKeys(parents[i].params); 102 | if (!parentParams.length) continue; 103 | 104 | for (var j in parentParams) { 105 | if (indexOf(inheritList, parentParams[j]) >= 0) continue; 106 | inheritList.push(parentParams[j]); 107 | inherited[parentParams[j]] = currentParams[parentParams[j]]; 108 | } 109 | } 110 | return extend({}, inherited, newParams); 111 | } 112 | 113 | /** 114 | * Performs a non-strict comparison of the subset of two objects, defined by a list of keys. 115 | * 116 | * @param {Object} a The first object. 117 | * @param {Object} b The second object. 118 | * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified, 119 | * it defaults to the list of keys in `a`. 120 | * @return {Boolean} Returns `true` if the keys match, otherwise `false`. 121 | */ 122 | function equalForKeys(a, b, keys) { 123 | if (!keys) { 124 | keys = []; 125 | for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility 126 | } 127 | 128 | for (var i=0; i 275 | * 276 | * 277 | * 278 | * 279 | * 280 | * 281 | * 285 | * 286 | * 287 | * 288 | * 289 | * 290 | */ 291 | angular.module('ui.router', ['ui.router.state']); 292 | 293 | angular.module('ui.router.compat', ['ui.router']); 294 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/src/resolve.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc object 3 | * @name ui.router.util.$resolve 4 | * 5 | * @requires $q 6 | * @requires $injector 7 | * 8 | * @description 9 | * Manages resolution of (acyclic) graphs of promises. 10 | */ 11 | $Resolve.$inject = ['$q', '$injector']; 12 | function $Resolve( $q, $injector) { 13 | 14 | var VISIT_IN_PROGRESS = 1, 15 | VISIT_DONE = 2, 16 | NOTHING = {}, 17 | NO_DEPENDENCIES = [], 18 | NO_LOCALS = NOTHING, 19 | NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING }); 20 | 21 | 22 | /** 23 | * @ngdoc function 24 | * @name ui.router.util.$resolve#study 25 | * @methodOf ui.router.util.$resolve 26 | * 27 | * @description 28 | * Studies a set of invocables that are likely to be used multiple times. 29 | *
 30 |    * $resolve.study(invocables)(locals, parent, self)
 31 |    * 
32 | * is equivalent to 33 | *
 34 |    * $resolve.resolve(invocables, locals, parent, self)
 35 |    * 
36 | * but the former is more efficient (in fact `resolve` just calls `study` 37 | * internally). 38 | * 39 | * @param {object} invocables Invocable objects 40 | * @return {function} a function to pass in locals, parent and self 41 | */ 42 | this.study = function (invocables) { 43 | if (!isObject(invocables)) throw new Error("'invocables' must be an object"); 44 | var invocableKeys = objectKeys(invocables || {}); 45 | 46 | // Perform a topological sort of invocables to build an ordered plan 47 | var plan = [], cycle = [], visited = {}; 48 | function visit(value, key) { 49 | if (visited[key] === VISIT_DONE) return; 50 | 51 | cycle.push(key); 52 | if (visited[key] === VISIT_IN_PROGRESS) { 53 | cycle.splice(0, indexOf(cycle, key)); 54 | throw new Error("Cyclic dependency: " + cycle.join(" -> ")); 55 | } 56 | visited[key] = VISIT_IN_PROGRESS; 57 | 58 | if (isString(value)) { 59 | plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES); 60 | } else { 61 | var params = $injector.annotate(value); 62 | forEach(params, function (param) { 63 | if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param); 64 | }); 65 | plan.push(key, value, params); 66 | } 67 | 68 | cycle.pop(); 69 | visited[key] = VISIT_DONE; 70 | } 71 | forEach(invocables, visit); 72 | invocables = cycle = visited = null; // plan is all that's required 73 | 74 | function isResolve(value) { 75 | return isObject(value) && value.then && value.$$promises; 76 | } 77 | 78 | return function (locals, parent, self) { 79 | if (isResolve(locals) && self === undefined) { 80 | self = parent; parent = locals; locals = null; 81 | } 82 | if (!locals) locals = NO_LOCALS; 83 | else if (!isObject(locals)) { 84 | throw new Error("'locals' must be an object"); 85 | } 86 | if (!parent) parent = NO_PARENT; 87 | else if (!isResolve(parent)) { 88 | throw new Error("'parent' must be a promise returned by $resolve.resolve()"); 89 | } 90 | 91 | // To complete the overall resolution, we have to wait for the parent 92 | // promise and for the promise for each invokable in our plan. 93 | var resolution = $q.defer(), 94 | result = resolution.promise, 95 | promises = result.$$promises = {}, 96 | values = extend({}, locals), 97 | wait = 1 + plan.length/3, 98 | merged = false; 99 | 100 | function done() { 101 | // Merge parent values we haven't got yet and publish our own $$values 102 | if (!--wait) { 103 | if (!merged) merge(values, parent.$$values); 104 | result.$$values = values; 105 | result.$$promises = result.$$promises || true; // keep for isResolve() 106 | delete result.$$inheritedValues; 107 | resolution.resolve(values); 108 | } 109 | } 110 | 111 | function fail(reason) { 112 | result.$$failure = reason; 113 | resolution.reject(reason); 114 | } 115 | 116 | // Short-circuit if parent has already failed 117 | if (isDefined(parent.$$failure)) { 118 | fail(parent.$$failure); 119 | return result; 120 | } 121 | 122 | if (parent.$$inheritedValues) { 123 | merge(values, omit(parent.$$inheritedValues, invocableKeys)); 124 | } 125 | 126 | // Merge parent values if the parent has already resolved, or merge 127 | // parent promises and wait if the parent resolve is still in progress. 128 | extend(promises, parent.$$promises); 129 | if (parent.$$values) { 130 | merged = merge(values, omit(parent.$$values, invocableKeys)); 131 | result.$$inheritedValues = omit(parent.$$values, invocableKeys); 132 | done(); 133 | } else { 134 | if (parent.$$inheritedValues) { 135 | result.$$inheritedValues = omit(parent.$$inheritedValues, invocableKeys); 136 | } 137 | parent.then(done, fail); 138 | } 139 | 140 | // Process each invocable in the plan, but ignore any where a local of the same name exists. 141 | for (var i=0, ii=plan.length; i 1 || e.ctrlKey || e.metaKey || e.shiftKey || el.attr('target'))) { 34 | // HACK: This is to allow ng-clicks to be processed before the transition is initiated: 35 | var transition = $timeout(function() { 36 | $state.go(target.state, target.params, target.options); 37 | }); 38 | e.preventDefault(); 39 | 40 | // if the state has no URL, ignore one preventDefault from the directive. 41 | var ignorePreventDefaultCount = type.isAnchor && !target.href ? 1: 0; 42 | 43 | e.preventDefault = function() { 44 | if (ignorePreventDefaultCount-- <= 0) $timeout.cancel(transition); 45 | }; 46 | } 47 | }; 48 | } 49 | 50 | function defaultOpts(el, $state) { 51 | return { relative: stateContext(el) || $state.$current, inherit: true }; 52 | } 53 | 54 | /** 55 | * @ngdoc directive 56 | * @name ui.router.state.directive:ui-sref 57 | * 58 | * @requires ui.router.state.$state 59 | * @requires $timeout 60 | * 61 | * @restrict A 62 | * 63 | * @description 64 | * A directive that binds a link (`` tag) to a state. If the state has an associated 65 | * URL, the directive will automatically generate & update the `href` attribute via 66 | * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking 67 | * the link will trigger a state transition with optional parameters. 68 | * 69 | * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be 70 | * handled natively by the browser. 71 | * 72 | * You can also use relative state paths within ui-sref, just like the relative 73 | * paths passed to `$state.go()`. You just need to be aware that the path is relative 74 | * to the state that the link lives in, in other words the state that loaded the 75 | * template containing the link. 76 | * 77 | * You can specify options to pass to {@link ui.router.state.$state#methods_go $state.go()} 78 | * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`, 79 | * and `reload`. 80 | * 81 | * @example 82 | * Here's an example of how you'd use ui-sref and how it would compile. If you have the 83 | * following template: 84 | *
 85 |  * Home | About | Next page
 86 |  *
 87 |  * 
 92 |  * 
93 | * 94 | * Then the compiled html would be (assuming Html5Mode is off and current state is contacts): 95 | *
 96 |  * Home | About | Next page
 97 |  *
 98 |  * 
    99 | *
  • 100 | * Joe 101 | *
  • 102 | *
  • 103 | * Alice 104 | *
  • 105 | *
  • 106 | * Bob 107 | *
  • 108 | *
109 | * 110 | * Home 111 | *
112 | * 113 | * @param {string} ui-sref 'stateName' can be any valid absolute or relative state 114 | * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#methods_go $state.go()} 115 | */ 116 | $StateRefDirective.$inject = ['$state', '$timeout']; 117 | function $StateRefDirective($state, $timeout) { 118 | return { 119 | restrict: 'A', 120 | require: ['?^uiSrefActive', '?^uiSrefActiveEq'], 121 | link: function(scope, element, attrs, uiSrefActive) { 122 | var ref = parseStateRef(attrs.uiSref, $state.current.name); 123 | var def = { state: ref.state, href: null, params: null }; 124 | var type = getTypeInfo(element); 125 | var active = uiSrefActive[1] || uiSrefActive[0]; 126 | var unlinkInfoFn = null; 127 | var hookFn; 128 | 129 | def.options = extend(defaultOpts(element, $state), attrs.uiSrefOpts ? scope.$eval(attrs.uiSrefOpts) : {}); 130 | 131 | var update = function(val) { 132 | if (val) def.params = angular.copy(val); 133 | def.href = $state.href(ref.state, def.params, def.options); 134 | 135 | if (unlinkInfoFn) unlinkInfoFn(); 136 | if (active) unlinkInfoFn = active.$$addStateInfo(ref.state, def.params); 137 | if (def.href !== null) attrs.$set(type.attr, def.href); 138 | }; 139 | 140 | if (ref.paramExpr) { 141 | scope.$watch(ref.paramExpr, function(val) { if (val !== def.params) update(val); }, true); 142 | def.params = angular.copy(scope.$eval(ref.paramExpr)); 143 | } 144 | update(); 145 | 146 | if (!type.clickable) return; 147 | hookFn = clickHook(element, $state, $timeout, type, function() { return def; }); 148 | element.bind("click", hookFn); 149 | scope.$on('$destroy', function() { 150 | element.unbind("click", hookFn); 151 | }); 152 | } 153 | }; 154 | } 155 | 156 | /** 157 | * @ngdoc directive 158 | * @name ui.router.state.directive:ui-state 159 | * 160 | * @requires ui.router.state.uiSref 161 | * 162 | * @restrict A 163 | * 164 | * @description 165 | * Much like ui-sref, but will accept named $scope properties to evaluate for a state definition, 166 | * params and override options. 167 | * 168 | * @param {string} ui-state 'stateName' can be any valid absolute or relative state 169 | * @param {Object} ui-state-params params to pass to {@link ui.router.state.$state#methods_href $state.href()} 170 | * @param {Object} ui-state-opts options to pass to {@link ui.router.state.$state#methods_go $state.go()} 171 | */ 172 | $StateRefDynamicDirective.$inject = ['$state', '$timeout']; 173 | function $StateRefDynamicDirective($state, $timeout) { 174 | return { 175 | restrict: 'A', 176 | require: ['?^uiSrefActive', '?^uiSrefActiveEq'], 177 | link: function(scope, element, attrs, uiSrefActive) { 178 | var type = getTypeInfo(element); 179 | var active = uiSrefActive[1] || uiSrefActive[0]; 180 | var group = [attrs.uiState, attrs.uiStateParams || null, attrs.uiStateOpts || null]; 181 | var watch = '[' + group.map(function(val) { return val || 'null'; }).join(', ') + ']'; 182 | var def = { state: null, params: null, options: null, href: null }; 183 | var unlinkInfoFn = null; 184 | var hookFn; 185 | 186 | function runStateRefLink (group) { 187 | def.state = group[0]; def.params = group[1]; def.options = group[2]; 188 | def.href = $state.href(def.state, def.params, def.options); 189 | 190 | if (unlinkInfoFn) unlinkInfoFn(); 191 | if (active) unlinkInfoFn = active.$$addStateInfo(def.state, def.params); 192 | if (def.href) attrs.$set(type.attr, def.href); 193 | } 194 | 195 | scope.$watch(watch, runStateRefLink, true); 196 | runStateRefLink(scope.$eval(watch)); 197 | 198 | if (!type.clickable) return; 199 | hookFn = clickHook(element, $state, $timeout, type, function() { return def; }); 200 | element.bind("click", hookFn); 201 | scope.$on('$destroy', function() { 202 | element.unbind("click", hookFn); 203 | }); 204 | } 205 | }; 206 | } 207 | 208 | 209 | /** 210 | * @ngdoc directive 211 | * @name ui.router.state.directive:ui-sref-active 212 | * 213 | * @requires ui.router.state.$state 214 | * @requires ui.router.state.$stateParams 215 | * @requires $interpolate 216 | * 217 | * @restrict A 218 | * 219 | * @description 220 | * A directive working alongside ui-sref to add classes to an element when the 221 | * related ui-sref directive's state is active, and removing them when it is inactive. 222 | * The primary use-case is to simplify the special appearance of navigation menus 223 | * relying on `ui-sref`, by having the "active" state's menu button appear different, 224 | * distinguishing it from the inactive menu items. 225 | * 226 | * ui-sref-active can live on the same element as ui-sref or on a parent element. The first 227 | * ui-sref-active found at the same level or above the ui-sref will be used. 228 | * 229 | * Will activate when the ui-sref's target state or any child state is active. If you 230 | * need to activate only when the ui-sref target state is active and *not* any of 231 | * it's children, then you will use 232 | * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq} 233 | * 234 | * @example 235 | * Given the following template: 236 | *
237 |  * 
242 |  * 
243 | * 244 | * 245 | * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins", 246 | * the resulting HTML will appear as (note the 'active' class): 247 | *
248 |  * 
253 |  * 
254 | * 255 | * The class name is interpolated **once** during the directives link time (any further changes to the 256 | * interpolated value are ignored). 257 | * 258 | * Multiple classes may be specified in a space-separated format: 259 | *
260 |  * 
    261 | *
  • 262 | * link 263 | *
  • 264 | *
265 | *
266 | * 267 | * It is also possible to pass ui-sref-active an expression that evaluates 268 | * to an object hash, whose keys represent active class names and whose 269 | * values represent the respective state names/globs. 270 | * ui-sref-active will match if the current active state **includes** any of 271 | * the specified state names/globs, even the abstract ones. 272 | * 273 | * @Example 274 | * Given the following template, with "admin" being an abstract state: 275 | *
276 |  * 
277 | * Roles 278 | *
279 | *
280 | * 281 | * When the current state is "admin.roles" the "active" class will be applied 282 | * to both the
and elements. It is important to note that the state 283 | * names/globs passed to ui-sref-active shadow the state provided by ui-sref. 284 | */ 285 | 286 | /** 287 | * @ngdoc directive 288 | * @name ui.router.state.directive:ui-sref-active-eq 289 | * 290 | * @requires ui.router.state.$state 291 | * @requires ui.router.state.$stateParams 292 | * @requires $interpolate 293 | * 294 | * @restrict A 295 | * 296 | * @description 297 | * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate 298 | * when the exact target state used in the `ui-sref` is active; no child states. 299 | * 300 | */ 301 | $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate']; 302 | function $StateRefActiveDirective($state, $stateParams, $interpolate) { 303 | return { 304 | restrict: "A", 305 | controller: ['$scope', '$element', '$attrs', '$timeout', function ($scope, $element, $attrs, $timeout) { 306 | var states = [], activeClasses = {}, activeEqClass, uiSrefActive; 307 | 308 | // There probably isn't much point in $observing this 309 | // uiSrefActive and uiSrefActiveEq share the same directive object with some 310 | // slight difference in logic routing 311 | activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope); 312 | 313 | try { 314 | uiSrefActive = $scope.$eval($attrs.uiSrefActive); 315 | } catch (e) { 316 | // Do nothing. uiSrefActive is not a valid expression. 317 | // Fall back to using $interpolate below 318 | } 319 | uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope); 320 | if (isObject(uiSrefActive)) { 321 | forEach(uiSrefActive, function(stateOrName, activeClass) { 322 | if (isString(stateOrName)) { 323 | var ref = parseStateRef(stateOrName, $state.current.name); 324 | addState(ref.state, $scope.$eval(ref.paramExpr), activeClass); 325 | } 326 | }); 327 | } 328 | 329 | // Allow uiSref to communicate with uiSrefActive[Equals] 330 | this.$$addStateInfo = function (newState, newParams) { 331 | // we already got an explicit state provided by ui-sref-active, so we 332 | // shadow the one that comes from ui-sref 333 | if (isObject(uiSrefActive) && states.length > 0) { 334 | return; 335 | } 336 | var deregister = addState(newState, newParams, uiSrefActive); 337 | update(); 338 | return deregister; 339 | }; 340 | 341 | $scope.$on('$stateChangeSuccess', update); 342 | 343 | function addState(stateName, stateParams, activeClass) { 344 | var state = $state.get(stateName, stateContext($element)); 345 | var stateHash = createStateHash(stateName, stateParams); 346 | 347 | var stateInfo = { 348 | state: state || { name: stateName }, 349 | params: stateParams, 350 | hash: stateHash 351 | }; 352 | 353 | states.push(stateInfo); 354 | activeClasses[stateHash] = activeClass; 355 | 356 | return function removeState() { 357 | var idx = states.indexOf(stateInfo); 358 | if (idx !== -1) states.splice(idx, 1); 359 | }; 360 | } 361 | 362 | /** 363 | * @param {string} state 364 | * @param {Object|string} [params] 365 | * @return {string} 366 | */ 367 | function createStateHash(state, params) { 368 | if (!isString(state)) { 369 | throw new Error('state should be a string'); 370 | } 371 | if (isObject(params)) { 372 | return state + toJson(params); 373 | } 374 | params = $scope.$eval(params); 375 | if (isObject(params)) { 376 | return state + toJson(params); 377 | } 378 | return state; 379 | } 380 | 381 | // Update route state 382 | function update() { 383 | for (var i = 0; i < states.length; i++) { 384 | if (anyMatch(states[i].state, states[i].params)) { 385 | addClass($element, activeClasses[states[i].hash]); 386 | } else { 387 | removeClass($element, activeClasses[states[i].hash]); 388 | } 389 | 390 | if (exactMatch(states[i].state, states[i].params)) { 391 | addClass($element, activeEqClass); 392 | } else { 393 | removeClass($element, activeEqClass); 394 | } 395 | } 396 | } 397 | 398 | function addClass(el, className) { $timeout(function () { el.addClass(className); }); } 399 | function removeClass(el, className) { el.removeClass(className); } 400 | function anyMatch(state, params) { return $state.includes(state.name, params); } 401 | function exactMatch(state, params) { return $state.is(state.name, params); } 402 | 403 | update(); 404 | }] 405 | }; 406 | } 407 | 408 | angular.module('ui.router.state') 409 | .directive('uiSref', $StateRefDirective) 410 | .directive('uiSrefActive', $StateRefActiveDirective) 411 | .directive('uiSrefActiveEq', $StateRefActiveDirective) 412 | .directive('uiState', $StateRefDynamicDirective); 413 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/src/stateFilters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc filter 3 | * @name ui.router.state.filter:isState 4 | * 5 | * @requires ui.router.state.$state 6 | * 7 | * @description 8 | * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}. 9 | */ 10 | $IsStateFilter.$inject = ['$state']; 11 | function $IsStateFilter($state) { 12 | var isFilter = function (state, params) { 13 | return $state.is(state, params); 14 | }; 15 | isFilter.$stateful = true; 16 | return isFilter; 17 | } 18 | 19 | /** 20 | * @ngdoc filter 21 | * @name ui.router.state.filter:includedByState 22 | * 23 | * @requires ui.router.state.$state 24 | * 25 | * @description 26 | * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}. 27 | */ 28 | $IncludedByStateFilter.$inject = ['$state']; 29 | function $IncludedByStateFilter($state) { 30 | var includesFilter = function (state, params, options) { 31 | return $state.includes(state, params, options); 32 | }; 33 | includesFilter.$stateful = true; 34 | return includesFilter; 35 | } 36 | 37 | angular.module('ui.router.state') 38 | .filter('isState', $IsStateFilter) 39 | .filter('includedByState', $IncludedByStateFilter); 40 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/src/templateFactory.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc object 3 | * @name ui.router.util.$templateFactory 4 | * 5 | * @requires $http 6 | * @requires $templateCache 7 | * @requires $injector 8 | * 9 | * @description 10 | * Service. Manages loading of templates. 11 | */ 12 | $TemplateFactory.$inject = ['$http', '$templateCache', '$injector']; 13 | function $TemplateFactory( $http, $templateCache, $injector) { 14 | 15 | /** 16 | * @ngdoc function 17 | * @name ui.router.util.$templateFactory#fromConfig 18 | * @methodOf ui.router.util.$templateFactory 19 | * 20 | * @description 21 | * Creates a template from a configuration object. 22 | * 23 | * @param {object} config Configuration object for which to load a template. 24 | * The following properties are search in the specified order, and the first one 25 | * that is defined is used to create the template: 26 | * 27 | * @param {string|object} config.template html string template or function to 28 | * load via {@link ui.router.util.$templateFactory#fromString fromString}. 29 | * @param {string|object} config.templateUrl url to load or a function returning 30 | * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}. 31 | * @param {Function} config.templateProvider function to invoke via 32 | * {@link ui.router.util.$templateFactory#fromProvider fromProvider}. 33 | * @param {object} params Parameters to pass to the template function. 34 | * @param {object} locals Locals to pass to `invoke` if the template is loaded 35 | * via a `templateProvider`. Defaults to `{ params: params }`. 36 | * 37 | * @return {string|object} The template html as a string, or a promise for 38 | * that string,or `null` if no template is configured. 39 | */ 40 | this.fromConfig = function (config, params, locals) { 41 | return ( 42 | isDefined(config.template) ? this.fromString(config.template, params) : 43 | isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) : 44 | isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) : 45 | null 46 | ); 47 | }; 48 | 49 | /** 50 | * @ngdoc function 51 | * @name ui.router.util.$templateFactory#fromString 52 | * @methodOf ui.router.util.$templateFactory 53 | * 54 | * @description 55 | * Creates a template from a string or a function returning a string. 56 | * 57 | * @param {string|object} template html template as a string or function that 58 | * returns an html template as a string. 59 | * @param {object} params Parameters to pass to the template function. 60 | * 61 | * @return {string|object} The template html as a string, or a promise for that 62 | * string. 63 | */ 64 | this.fromString = function (template, params) { 65 | return isFunction(template) ? template(params) : template; 66 | }; 67 | 68 | /** 69 | * @ngdoc function 70 | * @name ui.router.util.$templateFactory#fromUrl 71 | * @methodOf ui.router.util.$templateFactory 72 | * 73 | * @description 74 | * Loads a template from the a URL via `$http` and `$templateCache`. 75 | * 76 | * @param {string|Function} url url of the template to load, or a function 77 | * that returns a url. 78 | * @param {Object} params Parameters to pass to the url function. 79 | * @return {string|Promise.} The template html as a string, or a promise 80 | * for that string. 81 | */ 82 | this.fromUrl = function (url, params) { 83 | if (isFunction(url)) url = url(params); 84 | if (url == null) return null; 85 | else return $http 86 | .get(url, { cache: $templateCache, headers: { Accept: 'text/html' }}) 87 | .then(function(response) { return response.data; }); 88 | }; 89 | 90 | /** 91 | * @ngdoc function 92 | * @name ui.router.util.$templateFactory#fromProvider 93 | * @methodOf ui.router.util.$templateFactory 94 | * 95 | * @description 96 | * Creates a template by invoking an injectable provider function. 97 | * 98 | * @param {Function} provider Function to invoke via `$injector.invoke` 99 | * @param {Object} params Parameters for the template. 100 | * @param {Object} locals Locals to pass to `invoke`. Defaults to 101 | * `{ params: params }`. 102 | * @return {string|Promise.} The template html as a string, or a promise 103 | * for that string. 104 | */ 105 | this.fromProvider = function (provider, params, locals) { 106 | return $injector.invoke(provider, null, locals || { params: params }); 107 | }; 108 | } 109 | 110 | angular.module('ui.router.util').service('$templateFactory', $TemplateFactory); 111 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/src/urlRouter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc object 3 | * @name ui.router.router.$urlRouterProvider 4 | * 5 | * @requires ui.router.util.$urlMatcherFactoryProvider 6 | * @requires $locationProvider 7 | * 8 | * @description 9 | * `$urlRouterProvider` has the responsibility of watching `$location`. 10 | * When `$location` changes it runs through a list of rules one by one until a 11 | * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify 12 | * a url in a state configuration. All urls are compiled into a UrlMatcher object. 13 | * 14 | * There are several methods on `$urlRouterProvider` that make it useful to use directly 15 | * in your module config. 16 | */ 17 | $UrlRouterProvider.$inject = ['$locationProvider', '$urlMatcherFactoryProvider']; 18 | function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) { 19 | var rules = [], otherwise = null, interceptDeferred = false, listener; 20 | 21 | // Returns a string that is a prefix of all strings matching the RegExp 22 | function regExpPrefix(re) { 23 | var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source); 24 | return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : ''; 25 | } 26 | 27 | // Interpolates matched values into a String.replace()-style pattern 28 | function interpolate(pattern, match) { 29 | return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) { 30 | return match[what === '$' ? 0 : Number(what)]; 31 | }); 32 | } 33 | 34 | /** 35 | * @ngdoc function 36 | * @name ui.router.router.$urlRouterProvider#rule 37 | * @methodOf ui.router.router.$urlRouterProvider 38 | * 39 | * @description 40 | * Defines rules that are used by `$urlRouterProvider` to find matches for 41 | * specific URLs. 42 | * 43 | * @example 44 | *
 45 |    * var app = angular.module('app', ['ui.router.router']);
 46 |    *
 47 |    * app.config(function ($urlRouterProvider) {
 48 |    *   // Here's an example of how you might allow case insensitive urls
 49 |    *   $urlRouterProvider.rule(function ($injector, $location) {
 50 |    *     var path = $location.path(),
 51 |    *         normalized = path.toLowerCase();
 52 |    *
 53 |    *     if (path !== normalized) {
 54 |    *       return normalized;
 55 |    *     }
 56 |    *   });
 57 |    * });
 58 |    * 
59 | * 60 | * @param {function} rule Handler function that takes `$injector` and `$location` 61 | * services as arguments. You can use them to return a valid path as a string. 62 | * 63 | * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance 64 | */ 65 | this.rule = function (rule) { 66 | if (!isFunction(rule)) throw new Error("'rule' must be a function"); 67 | rules.push(rule); 68 | return this; 69 | }; 70 | 71 | /** 72 | * @ngdoc object 73 | * @name ui.router.router.$urlRouterProvider#otherwise 74 | * @methodOf ui.router.router.$urlRouterProvider 75 | * 76 | * @description 77 | * Defines a path that is used when an invalid route is requested. 78 | * 79 | * @example 80 | *
 81 |    * var app = angular.module('app', ['ui.router.router']);
 82 |    *
 83 |    * app.config(function ($urlRouterProvider) {
 84 |    *   // if the path doesn't match any of the urls you configured
 85 |    *   // otherwise will take care of routing the user to the
 86 |    *   // specified url
 87 |    *   $urlRouterProvider.otherwise('/index');
 88 |    *
 89 |    *   // Example of using function rule as param
 90 |    *   $urlRouterProvider.otherwise(function ($injector, $location) {
 91 |    *     return '/a/valid/url';
 92 |    *   });
 93 |    * });
 94 |    * 
95 | * 96 | * @param {string|function} rule The url path you want to redirect to or a function 97 | * rule that returns the url path. The function version is passed two params: 98 | * `$injector` and `$location` services, and must return a url string. 99 | * 100 | * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance 101 | */ 102 | this.otherwise = function (rule) { 103 | if (isString(rule)) { 104 | var redirect = rule; 105 | rule = function () { return redirect; }; 106 | } 107 | else if (!isFunction(rule)) throw new Error("'rule' must be a function"); 108 | otherwise = rule; 109 | return this; 110 | }; 111 | 112 | 113 | function handleIfMatch($injector, handler, match) { 114 | if (!match) return false; 115 | var result = $injector.invoke(handler, handler, { $match: match }); 116 | return isDefined(result) ? result : true; 117 | } 118 | 119 | /** 120 | * @ngdoc function 121 | * @name ui.router.router.$urlRouterProvider#when 122 | * @methodOf ui.router.router.$urlRouterProvider 123 | * 124 | * @description 125 | * Registers a handler for a given url matching. 126 | * 127 | * If the handler is a string, it is 128 | * treated as a redirect, and is interpolated according to the syntax of match 129 | * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise). 130 | * 131 | * If the handler is a function, it is injectable. It gets invoked if `$location` 132 | * matches. You have the option of inject the match object as `$match`. 133 | * 134 | * The handler can return 135 | * 136 | * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter` 137 | * will continue trying to find another one that matches. 138 | * - **string** which is treated as a redirect and passed to `$location.url()` 139 | * - **void** or any **truthy** value tells `$urlRouter` that the url was handled. 140 | * 141 | * @example 142 | *
143 |    * var app = angular.module('app', ['ui.router.router']);
144 |    *
145 |    * app.config(function ($urlRouterProvider) {
146 |    *   $urlRouterProvider.when($state.url, function ($match, $stateParams) {
147 |    *     if ($state.$current.navigable !== state ||
148 |    *         !equalForKeys($match, $stateParams) {
149 |    *      $state.transitionTo(state, $match, false);
150 |    *     }
151 |    *   });
152 |    * });
153 |    * 
154 | * 155 | * @param {string|object} what The incoming path that you want to redirect. 156 | * @param {string|function} handler The path you want to redirect your user to. 157 | */ 158 | this.when = function (what, handler) { 159 | var redirect, handlerIsString = isString(handler); 160 | if (isString(what)) what = $urlMatcherFactory.compile(what); 161 | 162 | if (!handlerIsString && !isFunction(handler) && !isArray(handler)) 163 | throw new Error("invalid 'handler' in when()"); 164 | 165 | var strategies = { 166 | matcher: function (what, handler) { 167 | if (handlerIsString) { 168 | redirect = $urlMatcherFactory.compile(handler); 169 | handler = ['$match', function ($match) { return redirect.format($match); }]; 170 | } 171 | return extend(function ($injector, $location) { 172 | return handleIfMatch($injector, handler, what.exec($location.path(), $location.search())); 173 | }, { 174 | prefix: isString(what.prefix) ? what.prefix : '' 175 | }); 176 | }, 177 | regex: function (what, handler) { 178 | if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky"); 179 | 180 | if (handlerIsString) { 181 | redirect = handler; 182 | handler = ['$match', function ($match) { return interpolate(redirect, $match); }]; 183 | } 184 | return extend(function ($injector, $location) { 185 | return handleIfMatch($injector, handler, what.exec($location.path())); 186 | }, { 187 | prefix: regExpPrefix(what) 188 | }); 189 | } 190 | }; 191 | 192 | var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp }; 193 | 194 | for (var n in check) { 195 | if (check[n]) return this.rule(strategies[n](what, handler)); 196 | } 197 | 198 | throw new Error("invalid 'what' in when()"); 199 | }; 200 | 201 | /** 202 | * @ngdoc function 203 | * @name ui.router.router.$urlRouterProvider#deferIntercept 204 | * @methodOf ui.router.router.$urlRouterProvider 205 | * 206 | * @description 207 | * Disables (or enables) deferring location change interception. 208 | * 209 | * If you wish to customize the behavior of syncing the URL (for example, if you wish to 210 | * defer a transition but maintain the current URL), call this method at configuration time. 211 | * Then, at run time, call `$urlRouter.listen()` after you have configured your own 212 | * `$locationChangeSuccess` event handler. 213 | * 214 | * @example 215 | *
216 |    * var app = angular.module('app', ['ui.router.router']);
217 |    *
218 |    * app.config(function ($urlRouterProvider) {
219 |    *
220 |    *   // Prevent $urlRouter from automatically intercepting URL changes;
221 |    *   // this allows you to configure custom behavior in between
222 |    *   // location changes and route synchronization:
223 |    *   $urlRouterProvider.deferIntercept();
224 |    *
225 |    * }).run(function ($rootScope, $urlRouter, UserService) {
226 |    *
227 |    *   $rootScope.$on('$locationChangeSuccess', function(e) {
228 |    *     // UserService is an example service for managing user state
229 |    *     if (UserService.isLoggedIn()) return;
230 |    *
231 |    *     // Prevent $urlRouter's default handler from firing
232 |    *     e.preventDefault();
233 |    *
234 |    *     UserService.handleLogin().then(function() {
235 |    *       // Once the user has logged in, sync the current URL
236 |    *       // to the router:
237 |    *       $urlRouter.sync();
238 |    *     });
239 |    *   });
240 |    *
241 |    *   // Configures $urlRouter's listener *after* your custom listener
242 |    *   $urlRouter.listen();
243 |    * });
244 |    * 
245 | * 246 | * @param {boolean} defer Indicates whether to defer location change interception. Passing 247 | no parameter is equivalent to `true`. 248 | */ 249 | this.deferIntercept = function (defer) { 250 | if (defer === undefined) defer = true; 251 | interceptDeferred = defer; 252 | }; 253 | 254 | /** 255 | * @ngdoc object 256 | * @name ui.router.router.$urlRouter 257 | * 258 | * @requires $location 259 | * @requires $rootScope 260 | * @requires $injector 261 | * @requires $browser 262 | * 263 | * @description 264 | * 265 | */ 266 | this.$get = $get; 267 | $get.$inject = ['$location', '$rootScope', '$injector', '$browser', '$sniffer']; 268 | function $get( $location, $rootScope, $injector, $browser, $sniffer) { 269 | 270 | var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl; 271 | 272 | function appendBasePath(url, isHtml5, absolute) { 273 | if (baseHref === '/') return url; 274 | if (isHtml5) return baseHref.slice(0, -1) + url; 275 | if (absolute) return baseHref.slice(1) + url; 276 | return url; 277 | } 278 | 279 | // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree 280 | function update(evt) { 281 | if (evt && evt.defaultPrevented) return; 282 | var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl; 283 | lastPushedUrl = undefined; 284 | // TODO: Re-implement this in 1.0 for https://github.com/angular-ui/ui-router/issues/1573 285 | //if (ignoreUpdate) return true; 286 | 287 | function check(rule) { 288 | var handled = rule($injector, $location); 289 | 290 | if (!handled) return false; 291 | if (isString(handled)) $location.replace().url(handled); 292 | return true; 293 | } 294 | var n = rules.length, i; 295 | 296 | for (i = 0; i < n; i++) { 297 | if (check(rules[i])) return; 298 | } 299 | // always check otherwise last to allow dynamic updates to the set of rules 300 | if (otherwise) check(otherwise); 301 | } 302 | 303 | function listen() { 304 | listener = listener || $rootScope.$on('$locationChangeSuccess', update); 305 | return listener; 306 | } 307 | 308 | if (!interceptDeferred) listen(); 309 | 310 | return { 311 | /** 312 | * @ngdoc function 313 | * @name ui.router.router.$urlRouter#sync 314 | * @methodOf ui.router.router.$urlRouter 315 | * 316 | * @description 317 | * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`. 318 | * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event, 319 | * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed 320 | * with the transition by calling `$urlRouter.sync()`. 321 | * 322 | * @example 323 | *
324 |        * angular.module('app', ['ui.router'])
325 |        *   .run(function($rootScope, $urlRouter) {
326 |        *     $rootScope.$on('$locationChangeSuccess', function(evt) {
327 |        *       // Halt state change from even starting
328 |        *       evt.preventDefault();
329 |        *       // Perform custom logic
330 |        *       var meetsRequirement = ...
331 |        *       // Continue with the update and state transition if logic allows
332 |        *       if (meetsRequirement) $urlRouter.sync();
333 |        *     });
334 |        * });
335 |        * 
336 | */ 337 | sync: function() { 338 | update(); 339 | }, 340 | 341 | listen: function() { 342 | return listen(); 343 | }, 344 | 345 | update: function(read) { 346 | if (read) { 347 | location = $location.url(); 348 | return; 349 | } 350 | if ($location.url() === location) return; 351 | 352 | $location.url(location); 353 | $location.replace(); 354 | }, 355 | 356 | push: function(urlMatcher, params, options) { 357 | var url = urlMatcher.format(params || {}); 358 | 359 | // Handle the special hash param, if needed 360 | if (url !== null && params && params['#']) { 361 | url += '#' + params['#']; 362 | } 363 | 364 | $location.url(url); 365 | lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined; 366 | if (options && options.replace) $location.replace(); 367 | }, 368 | 369 | /** 370 | * @ngdoc function 371 | * @name ui.router.router.$urlRouter#href 372 | * @methodOf ui.router.router.$urlRouter 373 | * 374 | * @description 375 | * A URL generation method that returns the compiled URL for a given 376 | * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters. 377 | * 378 | * @example 379 | *
380 |        * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
381 |        *   person: "bob"
382 |        * });
383 |        * // $bob == "/about/bob";
384 |        * 
385 | * 386 | * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate. 387 | * @param {object=} params An object of parameter values to fill the matcher's required parameters. 388 | * @param {object=} options Options object. The options are: 389 | * 390 | * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl". 391 | * 392 | * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher` 393 | */ 394 | href: function(urlMatcher, params, options) { 395 | if (!urlMatcher.validates(params)) return null; 396 | 397 | var isHtml5 = $locationProvider.html5Mode(); 398 | if (angular.isObject(isHtml5)) { 399 | isHtml5 = isHtml5.enabled; 400 | } 401 | 402 | isHtml5 = isHtml5 && $sniffer.history; 403 | 404 | var url = urlMatcher.format(params); 405 | options = options || {}; 406 | 407 | if (!isHtml5 && url !== null) { 408 | url = "#" + $locationProvider.hashPrefix() + url; 409 | } 410 | 411 | // Handle special hash param, if needed 412 | if (url !== null && params && params['#']) { 413 | url += '#' + params['#']; 414 | } 415 | 416 | url = appendBasePath(url, isHtml5, options.absolute); 417 | 418 | if (!options.absolute || !url) { 419 | return url; 420 | } 421 | 422 | var slash = (!isHtml5 && url ? '/' : ''), port = $location.port(); 423 | port = (port === 80 || port === 443 ? '' : ':' + port); 424 | 425 | return [$location.protocol(), '://', $location.host(), port, slash, url].join(''); 426 | } 427 | }; 428 | } 429 | } 430 | 431 | angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider); 432 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/src/view.js: -------------------------------------------------------------------------------- 1 | 2 | $ViewProvider.$inject = []; 3 | function $ViewProvider() { 4 | 5 | this.$get = $get; 6 | /** 7 | * @ngdoc object 8 | * @name ui.router.state.$view 9 | * 10 | * @requires ui.router.util.$templateFactory 11 | * @requires $rootScope 12 | * 13 | * @description 14 | * 15 | */ 16 | $get.$inject = ['$rootScope', '$templateFactory']; 17 | function $get( $rootScope, $templateFactory) { 18 | return { 19 | // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... }) 20 | /** 21 | * @ngdoc function 22 | * @name ui.router.state.$view#load 23 | * @methodOf ui.router.state.$view 24 | * 25 | * @description 26 | * 27 | * @param {string} name name 28 | * @param {object} options option object. 29 | */ 30 | load: function load(name, options) { 31 | var result, defaults = { 32 | template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {} 33 | }; 34 | options = extend(defaults, options); 35 | 36 | if (options.view) { 37 | result = $templateFactory.fromConfig(options.view, options.params, options.locals); 38 | } 39 | return result; 40 | } 41 | }; 42 | } 43 | } 44 | 45 | angular.module('ui.router.state').provider('$view', $ViewProvider); 46 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/src/viewDirective.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc directive 3 | * @name ui.router.state.directive:ui-view 4 | * 5 | * @requires ui.router.state.$state 6 | * @requires $compile 7 | * @requires $controller 8 | * @requires $injector 9 | * @requires ui.router.state.$uiViewScroll 10 | * @requires $document 11 | * 12 | * @restrict ECA 13 | * 14 | * @description 15 | * The ui-view directive tells $state where to place your templates. 16 | * 17 | * @param {string=} name A view name. The name should be unique amongst the other views in the 18 | * same state. You can have views of the same name that live in different states. 19 | * 20 | * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window 21 | * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll 22 | * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you 23 | * scroll ui-view elements into view when they are populated during a state activation. 24 | * 25 | * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) 26 | * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.* 27 | * 28 | * @param {string=} onload Expression to evaluate whenever the view updates. 29 | * 30 | * @example 31 | * A view can be unnamed or named. 32 | *
 33 |  * 
 34 |  * 
35 | * 36 | * 37 | *
38 | *
39 | * 40 | * You can only have one unnamed view within any template (or root html). If you are only using a 41 | * single view and it is unnamed then you can populate it like so: 42 | *
 43 |  * 
44 | * $stateProvider.state("home", { 45 | * template: "

HELLO!

" 46 | * }) 47 | *
48 | * 49 | * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#methods_state `views`} 50 | * config property, by name, in this case an empty name: 51 | *
 52 |  * $stateProvider.state("home", {
 53 |  *   views: {
 54 |  *     "": {
 55 |  *       template: "

HELLO!

" 56 | * } 57 | * } 58 | * }) 59 | *
60 | * 61 | * But typically you'll only use the views property if you name your view or have more than one view 62 | * in the same template. There's not really a compelling reason to name a view if its the only one, 63 | * but you could if you wanted, like so: 64 | *
 65 |  * 
66 | *
67 | *
 68 |  * $stateProvider.state("home", {
 69 |  *   views: {
 70 |  *     "main": {
 71 |  *       template: "

HELLO!

" 72 | * } 73 | * } 74 | * }) 75 | *
76 | * 77 | * Really though, you'll use views to set up multiple views: 78 | *
 79 |  * 
80 | *
81 | *
82 | *
83 | * 84 | *
 85 |  * $stateProvider.state("home", {
 86 |  *   views: {
 87 |  *     "": {
 88 |  *       template: "

HELLO!

" 89 | * }, 90 | * "chart": { 91 | * template: "" 92 | * }, 93 | * "data": { 94 | * template: "" 95 | * } 96 | * } 97 | * }) 98 | *
99 | * 100 | * Examples for `autoscroll`: 101 | * 102 | *
103 |  * 
105 |  * 
106 |  *
107 |  * 
109 |  * 
110 |  * 
111 |  * 
112 |  * 
113 | * 114 | * Resolve data: 115 | * 116 | * The resolved data from the state's `resolve` block is placed on the scope as `$resolve` (this 117 | * can be customized using [[ViewDeclaration.resolveAs]]). This can be then accessed from the template. 118 | * 119 | * Note that when `controllerAs` is being used, `$resolve` is set on the controller instance *after* the 120 | * controller is instantiated. The `$onInit()` hook can be used to perform initialization code which 121 | * depends on `$resolve` data. 122 | * 123 | * Example usage of $resolve in a view template 124 | *
125 |  * $stateProvider.state('home', {
126 |  *   template: '',
127 |  *   resolve: {
128 |  *     user: function(UserService) { return UserService.fetchUser(); }
129 |  *   }
130 |  * });
131 |  * 
132 | */ 133 | $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate', '$q']; 134 | function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate, $q) { 135 | 136 | function getService() { 137 | return ($injector.has) ? function(service) { 138 | return $injector.has(service) ? $injector.get(service) : null; 139 | } : function(service) { 140 | try { 141 | return $injector.get(service); 142 | } catch (e) { 143 | return null; 144 | } 145 | }; 146 | } 147 | 148 | var service = getService(), 149 | $animator = service('$animator'), 150 | $animate = service('$animate'); 151 | 152 | // Returns a set of DOM manipulation functions based on which Angular version 153 | // it should use 154 | function getRenderer(attrs, scope) { 155 | var statics = function() { 156 | return { 157 | enter: function (element, target, cb) { target.after(element); cb(); }, 158 | leave: function (element, cb) { element.remove(); cb(); } 159 | }; 160 | }; 161 | 162 | if ($animate) { 163 | return { 164 | enter: function(element, target, cb) { 165 | if (angular.version.minor > 2) { 166 | $animate.enter(element, null, target).then(cb); 167 | } else { 168 | $animate.enter(element, null, target, cb); 169 | } 170 | }, 171 | leave: function(element, cb) { 172 | if (angular.version.minor > 2) { 173 | $animate.leave(element).then(cb); 174 | } else { 175 | $animate.leave(element, cb); 176 | } 177 | } 178 | }; 179 | } 180 | 181 | if ($animator) { 182 | var animate = $animator && $animator(scope, attrs); 183 | 184 | return { 185 | enter: function(element, target, cb) {animate.enter(element, null, target); cb(); }, 186 | leave: function(element, cb) { animate.leave(element); cb(); } 187 | }; 188 | } 189 | 190 | return statics(); 191 | } 192 | 193 | var directive = { 194 | restrict: 'ECA', 195 | terminal: true, 196 | priority: 400, 197 | transclude: 'element', 198 | compile: function (tElement, tAttrs, $transclude) { 199 | return function (scope, $element, attrs) { 200 | var previousEl, currentEl, currentScope, latestLocals, 201 | onloadExp = attrs.onload || '', 202 | autoScrollExp = attrs.autoscroll, 203 | renderer = getRenderer(attrs, scope), 204 | inherited = $element.inheritedData('$uiView'); 205 | 206 | scope.$on('$stateChangeSuccess', function() { 207 | updateView(false); 208 | }); 209 | 210 | updateView(true); 211 | 212 | function cleanupLastView() { 213 | if (previousEl) { 214 | previousEl.remove(); 215 | previousEl = null; 216 | } 217 | 218 | if (currentScope) { 219 | currentScope.$destroy(); 220 | currentScope = null; 221 | } 222 | 223 | if (currentEl) { 224 | var $uiViewData = currentEl.data('$uiViewAnim'); 225 | renderer.leave(currentEl, function() { 226 | $uiViewData.$$animLeave.resolve(); 227 | previousEl = null; 228 | }); 229 | 230 | previousEl = currentEl; 231 | currentEl = null; 232 | } 233 | } 234 | 235 | function updateView(firstTime) { 236 | var newScope, 237 | name = getUiViewName(scope, attrs, $element, $interpolate), 238 | previousLocals = name && $state.$current && $state.$current.locals[name]; 239 | 240 | if (!firstTime && previousLocals === latestLocals) return; // nothing to do 241 | newScope = scope.$new(); 242 | latestLocals = $state.$current.locals[name]; 243 | 244 | /** 245 | * @ngdoc event 246 | * @name ui.router.state.directive:ui-view#$viewContentLoading 247 | * @eventOf ui.router.state.directive:ui-view 248 | * @eventType emits on ui-view directive scope 249 | * @description 250 | * 251 | * Fired once the view **begins loading**, *before* the DOM is rendered. 252 | * 253 | * @param {Object} event Event object. 254 | * @param {string} viewName Name of the view. 255 | */ 256 | newScope.$emit('$viewContentLoading', name); 257 | 258 | var clone = $transclude(newScope, function(clone) { 259 | var animEnter = $q.defer(), animLeave = $q.defer(); 260 | var viewAnimData = { 261 | $animEnter: animEnter.promise, 262 | $animLeave: animLeave.promise, 263 | $$animLeave: animLeave 264 | }; 265 | 266 | clone.data('$uiViewAnim', viewAnimData); 267 | renderer.enter(clone, $element, function onUiViewEnter() { 268 | animEnter.resolve(); 269 | if(currentScope) { 270 | currentScope.$emit('$viewContentAnimationEnded'); 271 | } 272 | 273 | if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) { 274 | $uiViewScroll(clone); 275 | } 276 | }); 277 | cleanupLastView(); 278 | }); 279 | 280 | currentEl = clone; 281 | currentScope = newScope; 282 | /** 283 | * @ngdoc event 284 | * @name ui.router.state.directive:ui-view#$viewContentLoaded 285 | * @eventOf ui.router.state.directive:ui-view 286 | * @eventType emits on ui-view directive scope 287 | * @description 288 | * Fired once the view is **loaded**, *after* the DOM is rendered. 289 | * 290 | * @param {Object} event Event object. 291 | * @param {string} viewName Name of the view. 292 | */ 293 | currentScope.$emit('$viewContentLoaded', name); 294 | currentScope.$eval(onloadExp); 295 | } 296 | }; 297 | } 298 | }; 299 | 300 | return directive; 301 | } 302 | 303 | $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate']; 304 | function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) { 305 | return { 306 | restrict: 'ECA', 307 | priority: -400, 308 | compile: function (tElement) { 309 | var initial = tElement.html(); 310 | return function (scope, $element, attrs) { 311 | var current = $state.$current, 312 | name = getUiViewName(scope, attrs, $element, $interpolate), 313 | locals = current && current.locals[name]; 314 | 315 | if (! locals) { 316 | return; 317 | } 318 | 319 | $element.data('$uiView', { name: name, state: locals.$$state }); 320 | $element.html(locals.$template ? locals.$template : initial); 321 | 322 | var resolveData = angular.extend({}, locals); 323 | scope[locals.$$resolveAs] = resolveData; 324 | 325 | var link = $compile($element.contents()); 326 | 327 | if (locals.$$controller) { 328 | locals.$scope = scope; 329 | locals.$element = $element; 330 | var controller = $controller(locals.$$controller, locals); 331 | if (locals.$$controllerAs) { 332 | scope[locals.$$controllerAs] = controller; 333 | scope[locals.$$controllerAs][locals.$$resolveAs] = resolveData; 334 | } 335 | if (isFunction(controller.$onInit)) controller.$onInit(); 336 | $element.data('$ngControllerController', controller); 337 | $element.children().data('$ngControllerController', controller); 338 | } 339 | 340 | link(scope); 341 | }; 342 | } 343 | }; 344 | } 345 | 346 | /** 347 | * Shared ui-view code for both directives: 348 | * Given scope, element, and its attributes, return the view's name 349 | */ 350 | function getUiViewName(scope, attrs, element, $interpolate) { 351 | var name = $interpolate(attrs.uiView || attrs.name || '')(scope); 352 | var uiViewCreatedBy = element.inheritedData('$uiView'); 353 | return name.indexOf('@') >= 0 ? name : (name + '@' + (uiViewCreatedBy ? uiViewCreatedBy.state.name : '')); 354 | } 355 | 356 | angular.module('ui.router.state').directive('uiView', $ViewDirective); 357 | angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill); 358 | -------------------------------------------------------------------------------- /bower_components/angular-ui-router/src/viewScroll.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc object 3 | * @name ui.router.state.$uiViewScrollProvider 4 | * 5 | * @description 6 | * Provider that returns the {@link ui.router.state.$uiViewScroll} service function. 7 | */ 8 | function $ViewScrollProvider() { 9 | 10 | var useAnchorScroll = false; 11 | 12 | /** 13 | * @ngdoc function 14 | * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll 15 | * @methodOf ui.router.state.$uiViewScrollProvider 16 | * 17 | * @description 18 | * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for 19 | * scrolling based on the url anchor. 20 | */ 21 | this.useAnchorScroll = function () { 22 | useAnchorScroll = true; 23 | }; 24 | 25 | /** 26 | * @ngdoc object 27 | * @name ui.router.state.$uiViewScroll 28 | * 29 | * @requires $anchorScroll 30 | * @requires $timeout 31 | * 32 | * @description 33 | * When called with a jqLite element, it scrolls the element into view (after a 34 | * `$timeout` so the DOM has time to refresh). 35 | * 36 | * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor, 37 | * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}. 38 | */ 39 | this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) { 40 | if (useAnchorScroll) { 41 | return $anchorScroll; 42 | } 43 | 44 | return function ($element) { 45 | return $timeout(function () { 46 | $element[0].scrollIntoView(); 47 | }, 0, false); 48 | }; 49 | }]; 50 | } 51 | 52 | angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider); 53 | -------------------------------------------------------------------------------- /bower_components/angular/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.5.7", 4 | "license": "MIT", 5 | "main": "./angular.js", 6 | "ignore": [], 7 | "dependencies": {}, 8 | "homepage": "https://github.com/angular/bower-angular", 9 | "_release": "1.5.7", 10 | "_resolution": { 11 | "type": "version", 12 | "tag": "v1.5.7", 13 | "commit": "8b7bc41468112797f501b2f6502a2be5c6a1bf5f" 14 | }, 15 | "_source": "https://github.com/angular/bower-angular.git", 16 | "_target": ">=1.4.0", 17 | "_originalSource": "angular" 18 | } -------------------------------------------------------------------------------- /bower_components/angular/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Angular 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bower_components/angular/README.md: -------------------------------------------------------------------------------- 1 | # packaged angular 2 | 3 | This repo is for distribution on `npm` and `bower`. The source for this module is in the 4 | [main AngularJS repo](https://github.com/angular/angular.js). 5 | Please file issues and pull requests against that repo. 6 | 7 | ## Install 8 | 9 | You can install this package either with `npm` or with `bower`. 10 | 11 | ### npm 12 | 13 | ```shell 14 | npm install angular 15 | ``` 16 | 17 | Then add a ` 21 | ``` 22 | 23 | Or `require('angular')` from your code. 24 | 25 | ### bower 26 | 27 | ```shell 28 | bower install angular 29 | ``` 30 | 31 | Then add a ` 35 | ``` 36 | 37 | ## Documentation 38 | 39 | Documentation is available on the 40 | [AngularJS docs site](http://docs.angularjs.org/). 41 | 42 | ## License 43 | 44 | The MIT License 45 | 46 | Copyright (c) 2010-2015 Google, Inc. http://angularjs.org 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a copy 49 | of this software and associated documentation files (the "Software"), to deal 50 | in the Software without restriction, including without limitation the rights 51 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 52 | copies of the Software, and to permit persons to whom the Software is 53 | furnished to do so, subject to the following conditions: 54 | 55 | The above copyright notice and this permission notice shall be included in 56 | all copies or substantial portions of the Software. 57 | 58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 64 | THE SOFTWARE. 65 | -------------------------------------------------------------------------------- /bower_components/angular/angular-csp.css: -------------------------------------------------------------------------------- 1 | /* Include this file in your html if you are using the CSP mode. */ 2 | 3 | @charset "UTF-8"; 4 | 5 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], 6 | .ng-cloak, .x-ng-cloak, 7 | .ng-hide:not(.ng-hide-animate) { 8 | display: none !important; 9 | } 10 | 11 | ng\:form { 12 | display: block; 13 | } 14 | 15 | .ng-animate-shim { 16 | visibility:hidden; 17 | } 18 | 19 | .ng-anchor { 20 | position:absolute; 21 | } 22 | -------------------------------------------------------------------------------- /bower_components/angular/angular.min.js.gzip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikemeding/Angular-Bootstrap-Modal-Forms/acfcf75e8cbd4de4a6297f02d53af842c5277012/bower_components/angular/angular.min.js.gzip -------------------------------------------------------------------------------- /bower_components/angular/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.5.7", 4 | "license": "MIT", 5 | "main": "./angular.js", 6 | "ignore": [], 7 | "dependencies": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /bower_components/angular/index.js: -------------------------------------------------------------------------------- 1 | require('./angular'); 2 | module.exports = angular; 3 | -------------------------------------------------------------------------------- /bower_components/angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.5.7", 4 | "description": "HTML enhanced for web apps", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/angular/angular.js.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "framework", 16 | "browser", 17 | "client-side" 18 | ], 19 | "author": "Angular Core Team ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/angular/angular.js/issues" 23 | }, 24 | "homepage": "http://angularjs.org" 25 | } 26 | -------------------------------------------------------------------------------- /readmeImages/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikemeding/Angular-Bootstrap-Modal-Forms/acfcf75e8cbd4de4a6297f02d53af842c5277012/readmeImages/Screenshot.png -------------------------------------------------------------------------------- /src/modalConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mike on 6/20/16. 3 | */ 4 | (function () { 5 | 'use strict'; 6 | 7 | // This creates the autoModals module with correct dependencies to make AnBoMoFo work. 8 | // It must be included before all other files in this directory. 9 | angular.module('autoModals', ['ui.bootstrap', 'ui.router']); 10 | }()); -------------------------------------------------------------------------------- /src/modalDirective.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Mike Meding 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Change Log: 25 | * Mon Aug 10 2015 Mike Meding 26 | * - initial beta version release 27 | * 28 | * Mon June 20 2016 Mike Meding 29 | * - fixed ui-bootstrap prefix issues 30 | * - added correct bower dependency structure 31 | * - created testing structure for future development and bug fixes 32 | * - updated anbomofo.js and anbomofo.min.js with bug fixes. 33 | * 34 | */ 35 | (function () { 36 | "use strict"; 37 | var module = angular.module("autoModals"); 38 | module.directive('modal', function () { 39 | /** 40 | * Credits and Examples used: 41 | * http://stackoverflow.com/questions/14115701/angularjs-create-a-directive-that-uses-ng-model // directives that use ng-model 42 | * http://stackoverflow.com/questions/20033493/angularjs-passing-service-as-an-argument-to-directive // for argument passing 43 | * http://www.html5rocks.com/en/tutorials/es6/promises/#toc-async // learning about promises 44 | * http://stackoverflow.com/questions/26807524/twitter-bootstrap-3-modal-with-scrollable-body // css modal body scrollbar magic 45 | * http://stackoverflow.com/questions/16529825/angularjs-ngclass-conditional // angular conditional classes for template 46 | * http://stackoverflow.com/questions/12139152/how-to-set-the-value-property-in-angularjs-ng-options // using ngOptions for select tags 47 | * http://odetocode.com/blogs/scott/archive/2013/06/19/using-ngoptions-in-angularjs.aspx // using ngOptions for select tags 48 | */ 49 | 50 | return { 51 | restrict: 'A', 52 | scope: { 53 | ngService: '@', 54 | ngModel: '=' // puts object directly in scope 55 | }, 56 | controller: [ 57 | '$scope', '$attrs', '$injector', '$uibModal', function ($scope, $attrs, $injector, $uibModal) { 58 | // inject service and get button label for this scope 59 | $scope.dataServiceName = $attrs.ngService; 60 | $scope.dataService = $injector.get($scope.dataServiceName); 61 | 62 | /** 63 | * Opens the modal 64 | * Based on the code from: 65 | * http://angular-ui.github.io/bootstrap/#/modal 66 | */ 67 | $scope.openModal = function () { 68 | 69 | function onwards() { 70 | $uibModal.open({ 71 | // this template is constructed from modalTemplate.html by converting the HTML into a giant string. 72 | template: ' ', 73 | controller: 'AddModalInstanceController', 74 | size: 'lg', 75 | resolve: { // this resolves the local vars of Instance Ctrl 76 | ngModel: function () { 77 | return $scope.ngModel; 78 | }, 79 | dataServiceName: function () { // send the modal instance the name of the service that it must inject 80 | return $scope.dataServiceName; 81 | } 82 | } 83 | }); 84 | } 85 | 86 | /** 87 | * if preClick exists execute it 88 | */ 89 | if (typeof $scope.dataService.preClick === "function") { // if preclick function exists 90 | $scope.dataService.preClick(); 91 | } 92 | 93 | 94 | function myTimeout() { 95 | setTimeout(function () { 96 | if (!$scope.ready) { 97 | $scope.ready = $scope.dataService.modelReady(); // if still not ready update 98 | myTimeout(); // run again 99 | } else { 100 | onwards(); // continue execution. 101 | } 102 | }, 100); // 100ms wait 103 | } 104 | 105 | /** 106 | * get relevant data for building our modal from our injected service 107 | * and do not return until that data is ready 108 | */ 109 | if (typeof $scope.dataService.modelReady === 'function') { 110 | $scope.ready = $scope.dataService.modelReady(); 111 | myTimeout(); // kick off synchronous timer 112 | } else { 113 | onwards(); // ignore and continue 114 | } 115 | 116 | 117 | }; 118 | } 119 | ], 120 | link: function compile($scope, element, $attrs, ctrl) { 121 | 122 | // bind the click handler to the element which has our modal-add attribute 123 | element.bind('click', function (e) { 124 | $scope.openModal(); // just open the stupid modal 125 | }); 126 | } 127 | } 128 | }); 129 | /** 130 | * The per instance modal controller. This handles the actual submission of the data 131 | */ 132 | module.controller("AddModalInstanceController", ['$scope', '$uibModalInstance', '$http', '$state', '$injector', 'dataServiceName', 'ngModel', 'ModalService', 133 | function ($scope, $uibModalInstance, $http, $state, $injector, dataServiceName, ngModel, ModalService) { 134 | // using service name to inject the service to our instance controller. 135 | var dataService = $injector.get(dataServiceName); 136 | 137 | var model = dataService.getModel(); 138 | // extract relevant settings 139 | $scope.fields = model['fields']; 140 | if (ngModel === undefined) { 141 | //console.log('add modal'); 142 | $scope.settings = model['addModalSettings']; 143 | } else { 144 | //console.log('edit modal'); 145 | $scope.settings = model['editModalSettings']; 146 | // set field values based on given model 147 | $scope.fields = ModalService.addValuesToFields(ngModel, $scope.fields); 148 | //console.log(ngModel); 149 | //console.log($scope.fields); 150 | } 151 | $scope.title = $scope.settings['title']; 152 | 153 | $scope.cancel = function () {// when cancel button is clicked 154 | $uibModalInstance.dismiss('cancel'); // this causes result promise to fail 155 | }; 156 | 157 | $scope.ok = function (fields) { // when ok button is clicked 158 | var newDataPoint = ModalService.getValuesFromFields(fields); // Extract data from fields 159 | 160 | /** 161 | * Checks only our requirements for validity 162 | */ 163 | var preformValidityCheckAndCallback = function () { // yay for long pointless function names 164 | // only do requirements validation 165 | $scope.fields = ModalService.checkValidRequirements($scope.fields); // call our requirements validation 166 | 167 | if (!ModalService.checkValidFromFields($scope.fields)) { // If any invalid states exist 168 | //console.error('invalid states exist'); 169 | $scope.fields = fields; 170 | } else { 171 | if ($scope.settings.hasOwnProperty('callback')) { 172 | $scope.settings['callback'](newDataPoint); // fire off successful callback function 173 | } 174 | $uibModalInstance.close(); // this causes result promise to succeed and modal to close and reset 175 | } 176 | }; 177 | 178 | // if user validation exists 179 | if ('validate' in $scope.settings) { 180 | $scope.fields = ModalService.resetValidity(fields); // reset validity fields for re-evaluation 181 | $scope.fields = $scope.settings['validate']($scope.fields); // call user defined validation service to update fields 182 | preformValidityCheckAndCallback(); 183 | } else { 184 | $scope.fields = ModalService.resetValidity(fields); // reset validity fields for re-evaluation 185 | preformValidityCheckAndCallback(); 186 | } 187 | }; 188 | 189 | // MODAL PROMISE 190 | $uibModalInstance.result.then(function (results) { // if successful submission 191 | $scope.fields = ModalService.resetModel(dataService.getModel()); // Reset modal 192 | }, function (err) { // if modal fails due to cancel or outside click 193 | $scope.fields = ModalService.resetModel(dataService.getModel()); // Reset modal 194 | }) 195 | 196 | }]); 197 | }()); 198 | -------------------------------------------------------------------------------- /src/modalService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Mike Meding 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Change Log: 25 | * Mon Aug 10 2015 Mike Meding 26 | * - initial beta version release 27 | */ 28 | (function () { 29 | var app = angular.module('autoModals'); 30 | app.service('ModalService', [function () { 31 | /** 32 | * Check that the model has the correct syntax for display 33 | * This serves only as a basic compile. This will need to become far more complex and involved 34 | * @param model 35 | */ 36 | this.compileModel = function (model) { 37 | var compileError = false; 38 | for (var x = 0; x < model['fields'].length; x++) { 39 | var field = model['fields'][x]; // shallow copy 40 | // if dropdown = type then dropdownOptions must also exist and contain at least 1 value 41 | if (field['type'] === 'dropdown' && !('dropdownOptions' in field)) { // if our type is a dropdown 42 | console.error('MODAL ERROR: dropdown field [' + field[name] + '] must have dropdownOptions Array'); 43 | compileError = true; 44 | } 45 | // name must exist 46 | if (!('name' in field)) { 47 | console.error('MODAL ERROR: name field must exist in field [' + JSON.stringify(field) + ']'); 48 | compileError = true; 49 | } 50 | // type must exist if display is true 51 | if (!('type' in field) && field['display']) { 52 | console.error('MODAL ERROR: type field must exist in field [' + JSON.stringify(field) + ']'); 53 | compileError = true; 54 | } 55 | } 56 | if (compileError) { 57 | console.error("Model compile error. Incorrectly formatted model."); 58 | return {}; 59 | } else { 60 | return addDefaultFields(model); // add default fields and return 61 | } 62 | 63 | }; 64 | 65 | /** 66 | * Add all default field parameters if they do not already exist 67 | * @param model 68 | * @returns {*} the updated model 69 | */ 70 | var addDefaultFields = function (model) { 71 | var updatedFields = {}; 72 | for (var x = 0; x < model['fields'].length; x++) { 73 | var field = model['fields'][x]; // shallow copy 74 | if (!('display' in field)) { // if display field does not exist 75 | field['display'] = true; 76 | } 77 | if (!('valid' in field)) { // for validation purposes 78 | field['valid'] = true; 79 | } 80 | if (!('required' in field)) { // if required field does not exist 81 | field['required'] = false; 82 | } 83 | if (!('displayName' in field)) { // if displayName field does not exist 84 | field['displayName'] = field['name']; // use required name field 85 | } 86 | if (!('placeholder' in field)) { // if placeholder field does not exist 87 | field['placeholder'] = 'Please enter a value'; 88 | } 89 | if (field['type'] === 'dropdown') { // if our type is a dropdown 90 | if (!field.hasOwnProperty('value')) { // if no default value is given 91 | field['value'] = field['dropdownOptions'][0]; // its inital value is the first item in dropdown option 92 | } 93 | } 94 | if (field['type'] === 'checkbox') { // if our type is a checkbox 95 | if (!field.hasOwnProperty('value')) { 96 | field['value'] = false; // default is false if nothing else 97 | } 98 | } 99 | if (field['type'] === 'datetime') { // if our type is a date picker 100 | if (!field.hasOwnProperty('value')) { 101 | field.value = new Date(); // default is the current time 102 | } 103 | } 104 | } 105 | return model; 106 | }; 107 | 108 | /** 109 | * Cleans up and returns our model so that it may be used upon reset. 110 | * @param model 111 | */ 112 | this.resetModel = function (model) { 113 | for (var x = 0; x < model['fields'].length; x++) { 114 | var field = model['fields'][x]; // shallow copy 115 | if (field['type'] === 'dropdown') { 116 | field['value'] = field['dropdownOptions'][0]; // its inital value is the first item in dropdown options 117 | } else { 118 | delete field['value'] 119 | } 120 | field['valid'] = true; // reset valid flag 121 | delete field['errorMessage']; // remove any error messages 122 | } 123 | return model; 124 | }; 125 | 126 | 127 | /** 128 | * Returns an object representing all the value fields of the given model 129 | * @param fields is an array of fields 130 | */ 131 | this.getValuesFromFields = function (fields) { 132 | var valueObj = {}; 133 | for (var x = 0; x < fields.length; x++) { // for all fields 134 | var field = fields[x]; // shallow copy 135 | if ('value' in field) { 136 | valueObj[field['name']] = field['value']; // creates key value pairs 137 | } else if (field['type'] === 'checkbox') { // if checkbox that has no value 138 | valueObj[field['name']] = false; // default false 139 | } 140 | 141 | } 142 | return valueObj; 143 | }; 144 | 145 | /** 146 | * Checks to see if all the fields have the valid flag set to true. returns false otherwise. 147 | * @param fields 148 | * @returns {boolean} 149 | */ 150 | this.checkValidFromFields = function (fields) { 151 | var valid = true; 152 | for (var x = 0; x < fields.length; x++) { // for all fields 153 | var field = fields[x]; // shallow copy 154 | if (!field['valid']) { // if false field exists 155 | valid = false; 156 | } 157 | } 158 | return valid; 159 | }; 160 | 161 | /** 162 | * Requirements check. This goes through and finds any fields marked as required and errors them out if 163 | * they have not been filled out. 164 | * @param fields 165 | * @returns {*} 166 | */ 167 | this.checkValidRequirements = function (fields) { 168 | for (var x = 0; x < fields.length; x++) { // for all fields 169 | var field = fields[x]; // shallow copy 170 | if ('required' in field) { // if the field is required 171 | if (field['required'] && field['valid'] && !('value' in field)) { // and it has no value and is still valid 172 | field['valid'] = false; // invalidate field 173 | field['errorMessage'] = 'This field is required'; 174 | } 175 | } 176 | } 177 | return fields; 178 | }; 179 | 180 | /** 181 | * Reset valid fields only for revalidation purposes 182 | * @param fields 183 | */ 184 | this.resetValidity = function (fields) { 185 | for (var x = 0; x < fields.length; x++) { // for all fields 186 | var field = fields[x]; // shallow copy 187 | field['valid'] = true; 188 | delete field['errorMessage']; 189 | } 190 | return fields; 191 | }; 192 | 193 | /** 194 | * Matches the names of those in values with those in fields adding values to fields. 195 | * @param values 196 | * @param fields 197 | */ 198 | this.addValuesToFields = function (values, fields) { 199 | for (var x = 0; x < fields.length; x++) { // for all fields 200 | var field = fields[x]; // shallow copy 201 | for (var key in values) { 202 | if (values.hasOwnProperty(key)) { // not sure why this needs to be set 203 | if (field['name'] === key) { 204 | //console.log('matched: ' + field['name']); 205 | field['value'] = values[key]; // set value accordingly 206 | } 207 | } 208 | } 209 | } 210 | return fields; 211 | }; 212 | 213 | }]); 214 | 215 | }()); 216 | -------------------------------------------------------------------------------- /src/modalTemplate.html: -------------------------------------------------------------------------------- 1 | 28 | 29 | 39 | 40 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /test/customers.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "OK", 3 | "version": 1.0, 4 | "list": [ 5 | { 6 | "id": 6, 7 | "customerName": "4 Energy", 8 | "active": true, 9 | "street": "6637 Front Street", 10 | "city": "North Anchorange", 11 | "usState": "AK", 12 | "email": "email@email.com", 13 | "phone": "555-555-5555" 14 | }, 15 | { 16 | "id": 9, 17 | "customerName": "Cara Donna", 18 | "active": false, 19 | "street": "7303 York Street", 20 | "city": "Winchester", 21 | "usState": "OH", 22 | "email": "email@email.com", 23 | "phone": "666-666-6666" 24 | }, 25 | { 26 | "id": 14, 27 | "customerName": "Colony Foods", 28 | "active": true, 29 | "street": "7303 York Street", 30 | "city": "Winchester", 31 | "usState": "OH", 32 | "email": "email@email.com", 33 | "phone": "666-666-6666" 34 | }, 35 | { 36 | "id": 4, 37 | "customerName": "Emerson", 38 | "active": false, 39 | "street": "6637 Front Street", 40 | "city": "North Anchorange", 41 | "usState": "AK", 42 | "email": "email@email.com", 43 | "phone": "555-555-5555" 44 | }, 45 | { 46 | "id": 8, 47 | "customerName": "Magniture", 48 | "active": true, 49 | "street": "7303 York Street", 50 | "city": "Winchester", 51 | "usState": "OH", 52 | "email": "email@email.com", 53 | "phone": "666-666-6666" 54 | }, 55 | { 56 | "id": 15, 57 | "customerName": "MBTA", 58 | "active": true, 59 | "street": "7303 York Street", 60 | "city": "Winchester", 61 | "usState": "OH", 62 | "email": "email@email.com", 63 | "phone": "666-666-6666" 64 | }, 65 | { 66 | "id": 10, 67 | "customerName": "Ice Cream", 68 | "active": false, 69 | "street": "6637 Front Street", 70 | "city": "North Anchorange", 71 | "usState": "AK", 72 | "email": "email@email.com", 73 | "phone": "555-555-5555" 74 | }, 75 | { 76 | "id": 11, 77 | "customerName": "Novartis", 78 | "active": true, 79 | "street": "7303 York Street", 80 | "city": "Winchester", 81 | "usState": "OH", 82 | "email": "email@email.com", 83 | "phone": "666-666-6666" 84 | }, 85 | { 86 | "id": 1, 87 | "customerName": "OutSmart Power Systems", 88 | "active": true, 89 | "street": "7303 York Street", 90 | "city": "Winchester", 91 | "usState": "OH", 92 | "email": "email@email.com", 93 | "phone": "666-666-6666" 94 | }, 95 | { 96 | "id": 7, 97 | "customerName": "Parker Hannifin", 98 | "active": true, 99 | "street": "6637 Front Street", 100 | "city": "North Anchorange", 101 | "usState": "AK", 102 | "email": "email@email.com", 103 | "phone": "555-555-5555" 104 | }, 105 | { 106 | "id": 5, 107 | "customerName": "Progress Software", 108 | "active": true, 109 | "street": "7303 York Street", 110 | "city": "Winchester", 111 | "usState": "OH", 112 | "email": "email@email.com", 113 | "phone": "666-666-6666" 114 | }, 115 | { 116 | "id": 13, 117 | "customerName": "Roche Brothers", 118 | "active": true, 119 | "street": "7303 York Street", 120 | "city": "Winchester", 121 | "usState": "OH", 122 | "email": "email@email.com", 123 | "phone": "666-666-6666" 124 | }, 125 | { 126 | "id": 2, 127 | "customerName": "Stop and Shop", 128 | "active": false, 129 | "street": "6637 Front Street", 130 | "city": "North Anchorange", 131 | "usState": "AK", 132 | "email": "email@email.com", 133 | "phone": "555-555-5555" 134 | }, 135 | { 136 | "id": 12, 137 | "customerName": "UTC", 138 | "active": true, 139 | "street": "7303 York Street", 140 | "city": "Winchester", 141 | "usState": "OH", 142 | "email": "email@email.com", 143 | "phone": "666-666-6666" 144 | }, 145 | { 146 | "id": 16, 147 | "customerName": "WCUPA", 148 | "active": true, 149 | "street": "7303 York Street", 150 | "city": "Winchester", 151 | "usState": "OH", 152 | "email": "email@email.com", 153 | "phone": "666-666-6666" 154 | } 155 | ] 156 | } -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |

Angular Data Driven Modals

49 |

Click on the "Add Data" button to launch the add data modal. Click on a table row to launch an edit data modal.

50 |

Further details, source code, and a detailed explanation can be found here in my github repo. 52 |

53 | 54 | 55 | 56 | 57 |
58 |
59 |

Data Table

60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 84 | 85 | 86 | 87 |
Customer NameStreet AddressCityStateActive
{{customer.customerName}}{{customer.street}}{{customer.city}}{{customer.usState}} 82 | 83 |
88 | 89 |
90 |
91 | 92 | 93 | -------------------------------------------------------------------------------- /test/testController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mike on 6/20/16. 3 | */ 4 | (function () { 5 | 'use strict'; 6 | var app = angular.module('app', ['ui.bootstrap', 'autoModals', 'ui.router']); 7 | 8 | app.controller('TestController', ['TestDataService', '$scope', '$http', function (TestDataService, $scope, $http) { 9 | 10 | $scope.customers = []; 11 | 12 | // fetch our data from the customers.json 13 | $scope.fetchCustomers = function () { 14 | var request = { 15 | method: 'GET', 16 | url: 'customers.json' 17 | }; 18 | $http(request) 19 | .success(function (data, status, headers, config, response) { 20 | $scope.customers = data.list; 21 | }) 22 | .error(function (data, status, headers, config, response) { 23 | console.error("Failed getting customers.json file"); 24 | }); 25 | }; 26 | $scope.fetchCustomers(); // on page load 27 | 28 | }]); 29 | }()); -------------------------------------------------------------------------------- /test/testDataService.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var app = angular.module('app'); 5 | 6 | app.service('TestDataService', ['ModalService', function (ModalService) { // bring in ModalService as a dependency (contains compiler) 7 | 8 | // submit functions 9 | this.addOnSubmit = function (data) { 10 | console.log(data); 11 | }; 12 | this.editOnSubmit = function (data) { 13 | console.log(data); 14 | }; 15 | 16 | var model = { 17 | fields: [ 18 | // making display false preserves the data but does not display it 19 | { 20 | name: "id", 21 | display: false 22 | }, 23 | 24 | // what a checkbox boolean looks like 25 | { 26 | name: "active", 27 | type: "checkbox", 28 | displayName: "Active?" 29 | }, 30 | { 31 | name: "customerName", 32 | displayName: "Customer Name", 33 | placeholder: "Please enter a persons name.", 34 | type: "text", 35 | required: true // constraints can be added as well 36 | }, 37 | { 38 | name: "street", 39 | displayName: "Street", 40 | type: "text", 41 | placeholder: "123 Street Rd" 42 | }, 43 | { 44 | name: "city", 45 | displayName: "City", 46 | type: "text", 47 | placeholder: "kalamazoo" 48 | }, 49 | 50 | // a dropdown with many options example 51 | { 52 | name: "usState", 53 | displayName: "State", 54 | type: "dropdown", 55 | dropdownOptions: [ 56 | "AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "DC", "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR", "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VI", "VA", "WA", "WV", "WI", "WY" 57 | ] 58 | }, 59 | { 60 | name: "email", 61 | displayName: "Email", 62 | type: "text", 63 | placeholder: "example@gmail.com" 64 | }, 65 | { 66 | name: "phone", 67 | displayName: "Phone", 68 | type: "text", 69 | placeholder: "(999)999-9999" 70 | }], 71 | 72 | // the callback functions are defined above 73 | addModalSettings: { 74 | title: 'Add New Person', 75 | callback: this.addOnSubmit 76 | }, 77 | editModalSettings: { 78 | title: 'Edit Person', 79 | callback: this.editOnSubmit 80 | } 81 | 82 | }; 83 | 84 | // you must compile and update your model 85 | model = ModalService.compileModel(model); 86 | 87 | // this method must be implemented to allow external access to your model 88 | this.getModel = function () { 89 | return model; 90 | }; 91 | 92 | }]); 93 | }()); 94 | /** 95 | * Created by mike on 6/20/16. 96 | */ 97 | --------------------------------------------------------------------------------