From 9d85ffd4ecaed20d70e4f48b278c992a159cda80 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Thu, 16 Jan 2014 16:48:02 -0600 Subject: [PATCH] update AngularUI Router to v0.2.8 --- dist/js/angular-ui/angular-ui-router.js | 1236 ++++++++++++++++--- dist/js/angular-ui/angular-ui-router.min.js | 4 +- 2 files changed, 1064 insertions(+), 176 deletions(-) diff --git a/dist/js/angular-ui/angular-ui-router.js b/dist/js/angular-ui/angular-ui-router.js index 109b5d0531..4a0e8157a2 100755 --- a/dist/js/angular-ui/angular-ui-router.js +++ b/dist/js/angular-ui/angular-ui-router.js @@ -1,6 +1,6 @@ /** * State-based routing for AngularJS - * @version v0.2.7 + * @version v0.2.8 * @link http://angular-ui.github.com/ * @license MIT License, http://www.opensource.org/licenses/MIT */ @@ -50,8 +50,7 @@ function ancestors(first, second) { var path = []; for (var n in first.path) { - if (first.path[n] === "") continue; - if (!second.path[n]) break; + if (first.path[n] !== second.path[n]) break; path.push(first.path[n]); } return path; @@ -177,18 +176,68 @@ function filterByKeys(keys, values) { return filtered; } +/** + * @ngdoc overview + * @name ui.router.util + * + * @description + * + */ angular.module('ui.router.util', ['ng']); -angular.module('ui.router.router', ['ui.router.util']); -angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']); -angular.module('ui.router', ['ui.router.state']); -angular.module('ui.router.compat', ['ui.router']); - /** - * Service (`ui-util`). Manages resolution of (acyclic) graphs of promises. - * @module $resolve + * @ngdoc overview + * @name ui.router.router + * + * @requires ui.router.util + * + * @description + * + */ +angular.module('ui.router.router', ['ui.router.util']); + +/** + * @ngdoc overview + * @name ui.router.router + * + * @requires ui.router.router + * @requires ui.router.util + * + * @description + * + */ +angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']); + +/** + * @ngdoc overview + * @name ui.router + * + * @requires ui.router.state + * + * @description + * + */ +angular.module('ui.router', ['ui.router.state']); +/** + * @ngdoc overview + * @name ui.router.compat + * + * @requires ui.router + * + * @description + * + */ +angular.module('ui.router.compat', ['ui.router']); + +/** + * @ngdoc object + * @name ui.router.util.$resolve + * * @requires $q * @requires $injector + * + * @description + * Manages resolution of (acyclic) graphs of promises. */ $Resolve.$inject = ['$q', '$injector']; function $Resolve( $q, $injector) { @@ -202,15 +251,24 @@ function $Resolve( $q, $injector) { /** + * @ngdoc function + * @name ui.router.util.$resolve#study + * @methodOf ui.router.util.$resolve + * + * @description * Studies a set of invocables that are likely to be used multiple times. - * $resolve.study(invocables)(locals, parent, self) + *
+   * $resolve.study(invocables)(locals, parent, self)
+   * 
* is equivalent to - * $resolve.resolve(invocables, locals, parent, self) - * but the former is more efficient (in fact `resolve` just calls `study` internally). - * See {@link module:$resolve/resolve} for details. - * @function - * @param {Object} invocables - * @return {Function} + *
+   * $resolve.resolve(invocables, locals, parent, self)
+   * 
+ * but the former is more efficient (in fact `resolve` just calls `study` + * internally). + * + * @param {object} invocables Invocable objects + * @return {function} a function to pass in locals, parent and self */ this.study = function (invocables) { if (!isObject(invocables)) throw new Error("'invocables' must be an object"); @@ -346,51 +404,65 @@ function $Resolve( $q, $injector) { }; /** - * Resolves a set of invocables. An invocable is a function to be invoked via `$injector.invoke()`, - * and can have an arbitrary number of dependencies. An invocable can either return a value directly, - * or a `$q` promise. If a promise is returned it will be resolved and the resulting value will be - * used instead. Dependencies of invocables are resolved (in this order of precedence) + * @ngdoc function + * @name ui.router.util.$resolve#resolve + * @methodOf ui.router.util.$resolve + * + * @description + * Resolves a set of invocables. An invocable is a function to be invoked via + * `$injector.invoke()`, and can have an arbitrary number of dependencies. + * An invocable can either return a value directly, + * or a `$q` promise. If a promise is returned it will be resolved and the + * resulting value will be used instead. Dependencies of invocables are resolved + * (in this order of precedence) * * - from the specified `locals` * - from another invocable that is part of this `$resolve` call - * - from an invocable that is inherited from a `parent` call to `$resolve` (or recursively - * from any ancestor `$resolve` of that parent). + * - from an invocable that is inherited from a `parent` call to `$resolve` + * (or recursively + * - from any ancestor `$resolve` of that parent). * - * The return value of `$resolve` is a promise for an object that contains (in this order of precedence) + * The return value of `$resolve` is a promise for an object that contains + * (in this order of precedence) * * - any `locals` (if specified) * - the resolved return values of all injectables * - any values inherited from a `parent` call to `$resolve` (if specified) * - * The promise will resolve after the `parent` promise (if any) and all promises returned by injectables - * have been resolved. If any invocable (or `$injector.invoke`) throws an exception, or if a promise - * returned by an invocable is rejected, the `$resolve` promise is immediately rejected with the same error. - * A rejection of a `parent` promise (if specified) will likewise be propagated immediately. Once the - * `$resolve` promise has been rejected, no further invocables will be called. + * The promise will resolve after the `parent` promise (if any) and all promises + * returned by injectables have been resolved. If any invocable + * (or `$injector.invoke`) throws an exception, or if a promise returned by an + * invocable is rejected, the `$resolve` promise is immediately rejected with the + * same error. A rejection of a `parent` promise (if specified) will likewise be + * propagated immediately. Once the `$resolve` promise has been rejected, no + * further invocables will be called. * - * Cyclic dependencies between invocables are not permitted and will caues `$resolve` to throw an - * error. As a special case, an injectable can depend on a parameter with the same name as the injectable, - * which will be fulfilled from the `parent` injectable of the same name. This allows inherited values - * to be decorated. Note that in this case any other injectable in the same `$resolve` with the same + * Cyclic dependencies between invocables are not permitted and will caues `$resolve` + * to throw an error. As a special case, an injectable can depend on a parameter + * with the same name as the injectable, which will be fulfilled from the `parent` + * injectable of the same name. This allows inherited values to be decorated. + * Note that in this case any other injectable in the same `$resolve` with the same * dependency would see the decorated value, not the inherited value. * - * Note that missing dependencies -- unlike cyclic dependencies -- will cause an (asynchronous) rejection - * of the `$resolve` promise rather than a (synchronous) exception. + * Note that missing dependencies -- unlike cyclic dependencies -- will cause an + * (asynchronous) rejection of the `$resolve` promise rather than a (synchronous) + * exception. * - * Invocables are invoked eagerly as soon as all dependencies are available. This is true even for - * dependencies inherited from a `parent` call to `$resolve`. + * Invocables are invoked eagerly as soon as all dependencies are available. + * This is true even for dependencies inherited from a `parent` call to `$resolve`. * - * As a special case, an invocable can be a string, in which case it is taken to be a service name - * to be passed to `$injector.get()`. This is supported primarily for backwards-compatibility with the - * `resolve` property of `$routeProvider` routes. + * As a special case, an invocable can be a string, in which case it is taken to + * be a service name to be passed to `$injector.get()`. This is supported primarily + * for backwards-compatibility with the `resolve` property of `$routeProvider` + * routes. * - * @function - * @param {Object.} invocables functions to invoke or `$injector` services to fetch. - * @param {Object.} [locals] values to make available to the injectables - * @param {Promise.} [parent] a promise returned by another call to `$resolve`. - * @param {Object} [self] the `this` for the invoked methods - * @return {Promise.} Promise for an object that contains the resolved return value - * of all invocables, as well as any inherited and local values. + * @param {object} invocables functions to invoke or + * `$injector` services to fetch. + * @param {object} locals values to make available to the injectables + * @param {object} parent a promise returned by another call to `$resolve`. + * @param {object} self the `this` for the invoked methods + * @return {object} Promise for an object that contains the resolved return value + * of all invocables, as well as any inherited and local values. */ this.resolve = function (invocables, locals, parent, self) { return this.study(invocables)(locals, parent, self); @@ -401,35 +473,43 @@ angular.module('ui.router.util').service('$resolve', $Resolve); /** - * Service. Manages loading of templates. - * @constructor - * @name $templateFactory + * @ngdoc object + * @name ui.router.util.$templateFactory + * * @requires $http * @requires $templateCache * @requires $injector + * + * @description + * Service. Manages loading of templates. */ $TemplateFactory.$inject = ['$http', '$templateCache', '$injector']; function $TemplateFactory( $http, $templateCache, $injector) { /** + * @ngdoc function + * @name ui.router.util.$templateFactory#fromConfig + * @methodOf ui.router.util.$templateFactory + * + * @description * Creates a template from a configuration object. - * @function - * @name $templateFactory#fromConfig - * @methodOf $templateFactory - * @param {Object} config Configuration object for which to load a template. The following - * properties are search in the specified order, and the first one that is defined is - * used to create the template: - * @param {string|Function} config.template html string template or function to load via - * {@link $templateFactory#fromString fromString}. - * @param {string|Function} config.templateUrl url to load or a function returning the url - * to load via {@link $templateFactory#fromUrl fromUrl}. - * @param {Function} config.templateProvider function to invoke via - * {@link $templateFactory#fromProvider fromProvider}. - * @param {Object} params Parameters to pass to the template function. - * @param {Object} [locals] Locals to pass to `invoke` if the template is loaded via a - * `templateProvider`. Defaults to `{ params: params }`. - * @return {string|Promise.} The template html as a string, or a promise for that string, - * or `null` if no template is configured. + * + * @param {object} config Configuration object for which to load a template. + * The following properties are search in the specified order, and the first one + * that is defined is used to create the template: + * + * @param {string|object} config.template html string template or function to + * load via {@link ui.router.util.$templateFactory#fromString fromString}. + * @param {string|object} config.templateUrl url to load or a function returning + * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}. + * @param {Function} config.templateProvider function to invoke via + * {@link ui.router.util.$templateFactory#fromProvider fromProvider}. + * @param {object} params Parameters to pass to the template function. + * @param {object} locals Locals to pass to `invoke` if the template is loaded + * via a `templateProvider`. Defaults to `{ params: params }`. + * + * @return {string|object} The template html as a string, or a promise for + * that string,or `null` if no template is configured. */ this.fromConfig = function (config, params, locals) { return ( @@ -441,27 +521,37 @@ function $TemplateFactory( $http, $templateCache, $injector) { }; /** + * @ngdoc function + * @name ui.router.util.$templateFactory#fromString + * @methodOf ui.router.util.$templateFactory + * + * @description * Creates a template from a string or a function returning a string. - * @function - * @name $templateFactory#fromString - * @methodOf $templateFactory - * @param {string|Function} template html template as a string or function that returns an html - * template as a string. - * @param {Object} params Parameters to pass to the template function. - * @return {string|Promise.} The template html as a string, or a promise for that string. + * + * @param {string|object} template html template as a string or function that + * returns an html template as a string. + * @param {object} params Parameters to pass to the template function. + * + * @return {string|object} The template html as a string, or a promise for that + * string. */ this.fromString = function (template, params) { return isFunction(template) ? template(params) : template; }; /** + * @ngdoc function + * @name ui.router.util.$templateFactory#fromUrl + * @methodOf ui.router.util.$templateFactory + * + * @description * Loads a template from the a URL via `$http` and `$templateCache`. - * @function - * @name $templateFactory#fromUrl - * @methodOf $templateFactory - * @param {string|Function} url url of the template to load, or a function that returns a url. - * @param {Object} params Parameters to pass to the url function. - * @return {string|Promise.} The template html as a string, or a promise for that string. + * + * @param {string|Function} url url of the template to load, or a function + * that returns a url. + * @param {Object} params Parameters to pass to the url function. + * @return {string|Promise.} The template html as a string, or a promise + * for that string. */ this.fromUrl = function (url, params) { if (isFunction(url)) url = url(params); @@ -472,14 +562,19 @@ function $TemplateFactory( $http, $templateCache, $injector) { }; /** + * @ngdoc function + * @name ui.router.util.$templateFactory#fromUrl + * @methodOf ui.router.util.$templateFactory + * + * @description * Creates a template by invoking an injectable provider function. - * @function - * @name $templateFactory#fromUrl - * @methodOf $templateFactory + * * @param {Function} provider Function to invoke via `$injector.invoke` * @param {Object} params Parameters for the template. - * @param {Object} [locals] Locals to pass to `invoke`. Defaults to `{ params: params }`. - * @return {string|Promise.} The template html as a string, or a promise for that string. + * @param {Object} locals Locals to pass to `invoke`. Defaults to + * `{ params: params }`. + * @return {string|Promise.} The template html as a string, or a promise + * for that string. */ this.fromProvider = function (provider, params, locals) { return $injector.invoke(provider, null, locals || { params: params }); @@ -745,7 +840,21 @@ function $UrlMatcherFactory() { // Register as a provider so it's available to other providers angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory); - +/** + * @ngdoc object + * @name ui.router.router.$urlRouterProvider + * + * @requires ui.router.util.$urlMatcherFactoryProvider + * + * @description + * `$urlRouterProvider` has the responsibility of watching `$location`. + * When `$location` changes it runs through a list of rules one by one until a + * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify + * a url in a state configuration. All urls are compiled into a UrlMatcher object. + * + * There are several methods on `$urlRouterProvider` that make it useful to use directly + * in your module config. + */ $UrlRouterProvider.$inject = ['$urlMatcherFactoryProvider']; function $UrlRouterProvider( $urlMatcherFactory) { var rules = [], @@ -764,6 +873,37 @@ function $UrlRouterProvider( $urlMatcherFactory) { }); } + /** + * @ngdoc function + * @name ui.router.router.$urlRouterProvider#rule + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Defines rules that are used by `$urlRouterProvider to find matches for + * specific URLs. + * + * @example + *
+   * var app = angular.module('app', ['ui.router.router']);
+   *
+   * app.config(function ($urlRouterProvider) {
+   *   // Here's an example of how you might allow case insensitive urls
+   *   $urlRouterProvider.rule(function ($injector, $location) {
+   *     var path = $location.path(),
+   *         normalized = path.toLowerCase();
+   *
+   *     if (path !== normalized) {
+   *       return normalized;
+   *     }
+   *   });
+   * });
+   * 
+ * + * @param {object} rule Handler function that takes `$injector` and `$location` + * services as arguments. You can use them to return a valid path as a string. + * + * @return {object} $urlRouterProvider - $urlRouterProvider instance + */ this.rule = function (rule) { if (!isFunction(rule)) throw new Error("'rule' must be a function"); @@ -771,6 +911,37 @@ function $UrlRouterProvider( $urlMatcherFactory) { return this; }; + /** + * @ngdoc object + * @name ui.router.router.$urlRouterProvider#otherwise + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Defines a path that is used when an invalied route is requested. + * + * @example + *
+   * var app = angular.module('app', ['ui.router.router']);
+   *
+   * app.config(function ($urlRouterProvider) {
+   *   // if the path doesn't match any of the urls you configured
+   *   // otherwise will take care of routing the user to the
+   *   // specified url
+   *   $urlRouterProvider.otherwise('/index');
+   *
+   *   // Example of using function rule as param
+   *   $urlRouterProvider.otherwise(function ($injector, $location) {
+   *     ...
+   *   });
+   * });
+   * 
+ * + * @param {string|object} rule The url path you want to redirect to or a function + * rule that returns the url path. The function version is passed two params: + * `$injector` and `$location` services. + * + * @return {object} $urlRouterProvider - $urlRouterProvider instance + */ this.otherwise = function (rule) { if (isString(rule)) { @@ -789,6 +960,43 @@ function $UrlRouterProvider( $urlMatcherFactory) { return isDefined(result) ? result : true; } + /** + * @ngdoc function + * @name ui.router.router.$urlRouterProvider#when + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Registers a handler for a given url matching. if handle is a string, it is + * treated as a redirect, and is interpolated according to the syyntax of match + * (i.e. like String.replace() for RegExp, or like a UrlMatcher pattern otherwise). + * + * If the handler is a function, it is injectable. It gets invoked if `$location` + * matches. You have the option of inject the match object as `$match`. + * + * The handler can return + * + * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter` + * will continue trying to find another one that matches. + * - **string** which is treated as a redirect and passed to `$location.url()` + * - **void** or any **truthy** value tells `$urlRouter` that the url was handled. + * + * @example + *
+   * var app = angular.module('app', ['ui.router.router']);
+   *
+   * app.config(function ($urlRouterProvider) {
+   *   $urlRouterProvider.when($state.url, function ($match, $stateParams) {
+   *     if ($state.$current.navigable !== state ||
+   *         !equalForKeys($match, $stateParams) {
+   *      $state.transitionTo(state, $match, false);
+   *     }
+   *   });
+   * });
+   * 
+ * + * @param {string|object} what The incoming path that you want to redirect. + * @param {string|object} handler The path you want to redirect your user to. + */ this.when = function (what, handler) { var redirect, handlerIsString = isString(handler); @@ -835,6 +1043,17 @@ function $UrlRouterProvider( $urlMatcherFactory) { throw new Error("invalid 'what' in when()"); }; + /** + * @ngdoc object + * @name ui.router.router.$urlRouter + * + * @requires $location + * @requires $rootScope + * @requires $injector + * + * @description + * + */ this.$get = [ '$location', '$rootScope', '$injector', function ($location, $rootScope, $injector) { @@ -860,6 +1079,32 @@ function $UrlRouterProvider( $urlMatcherFactory) { $rootScope.$on('$locationChangeSuccess', update); return { + /** + * @ngdoc function + * @name ui.router.router.$urlRouter#sync + * @methodOf ui.router.router.$urlRouter + * + * @description + * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`. + * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event, + * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed + * with the transition by calling `$urlRouter.sync()`. + * + * @example + *
+         * angular.module('app', ['ui.router']);
+         *   .run(function($rootScope, $urlRouter) {
+         *     $rootScope.$on('$locationChangeSuccess', function(evt) {
+         *       // Halt state change from even starting
+         *       evt.preventDefault();
+         *       // Perform custom logic
+         *       var meetsRequirement = ...
+         *       // Continue with the update and state transition if logic allows
+         *       if (meetsRequirement) $urlRouter.sync();
+         *     });
+         * });
+         * 
+ */ sync: function () { update(); } @@ -869,6 +1114,28 @@ function $UrlRouterProvider( $urlMatcherFactory) { angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider); +/** + * @ngdoc object + * @name ui.router.state.$stateProvider + * + * @requires ui.router.router.$urlRouterProvider + * @requires ui.router.util.$urlMatcherFactoryProvider + * @requires $locationProvider + * + * @description + * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely + * on state. + * + * A state corresponds to a "place" in the application in terms of the overall UI and + * navigation. A state describes (via the controller / template / view properties) what + * the UI looks like and does at that place. + * + * States often have things in common, and the primary way of factoring out these + * commonalities in this model is via the state hierarchy, i.e. parent/child states aka + * nested states. + * + * The `$stateProvider` provides interfaces to declare these states for your app. + */ $StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider', '$locationProvider']; function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $locationProvider) { @@ -1078,9 +1345,96 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ root.navigable = null; - // .decorator() - // .decorator(name) - // .decorator(name, function) + /** + * @ngdoc function + * @name ui.router.state.$stateProvider#decorator + * @methodOf ui.router.state.$stateProvider + * + * @description + * Allows you to extend (carefully) or override (at your own peril) the + * `stateBuilder` object used internally by `$stateProvider`. This can be used + * to add custom functionality to ui-router, for example inferring templateUrl + * based on the state name. + * + * When passing only a name, it returns the current (original or decorated) builder + * function that matches `name`. + * + * The builder functions that can be decorated are listed below. Though not all + * necessarily have a good use case for decoration, that is up to you to decide. + * + * In addition, users can attach custom decorators, which will generate new + * properties within the state's internal definition. There is currently no clear + * use-case for this beyond accessing internal states (i.e. $state.$current), + * however, expect this to become increasingly relevant as we introduce additional + * meta-programming features. + * + * **Warning**: Decorators should not be interdependent because the order of + * execution of the builder functions in nondeterministic. Builder functions + * should only be dependent on the state definition object and super function. + * + * + * Existing builder functions and current return values: + * + * - parent - `{object}` - returns the parent state object. + * - data - `{object}` - returns state data, including any inherited data that is not + * overridden by own values (if any). + * - url - `{object}` - returns a UrlMatcher or null. + * - navigable - returns closest ancestor state that has a URL (aka is + * navigable). + * - params - `{object}` - returns an array of state params that are ensured to + * be a super-set of parent's params. + * - views - `{object}` - returns a views object where each key is an absolute view + * name (i.e. "viewName@stateName") and each value is the config object + * (template, controller) for the view. Even when you don't use the views object + * explicitly on a state config, one is still created for you internally. + * So by decorating this builder function you have access to decorating template + * and controller properties. + * - ownParams - `{object}` - returns an array of params that belong to the state, + * not including any params defined by ancestor states. + * - path - `{string}` - returns the full path from the root down to this state. + * Needed for state activation. + * - includes - `{object}` - returns an object that includes every state that + * would pass a '$state.includes()' test. + * + * @example + *
+   * // Override the internal 'views' builder with a function that takes the state
+   * // definition, and a reference to the internal function being overridden:
+   * $stateProvider.decorator('views', function ($state, parent) {
+   *   var result = {},
+   *       views = parent(state);
+   *
+   *   angular.forEach(view, function (config, name) {
+   *     var autoName = (state.name + '.' + name).replace('.', '/');
+   *     config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
+   *     result[name] = config;
+   *   });
+   *   return result;
+   * });
+   *
+   * $stateProvider.state('home', {
+   *   views: {
+   *     'contact.list': { controller: 'ListController' },
+   *     'contact.item': { controller: 'ItemController' }
+   *   }
+   * });
+   *
+   * // ...
+   *
+   * $state.go('home');
+   * // Auto-populates list and item views with /partials/home/contact/list.html,
+   * // and /partials/home/contact/item.html, respectively.
+   * 
+ * + * @param {string} name The name of the builder function to decorate. + * @param {object} func A function that is responsible for decorating the original + * builder function. The function receives two parameters: + * + * - `{object}` - state - The state config object. + * - `{object}` - super - The original builder function. + * + * @return {object} $stateProvider - $stateProvider instance + */ this.decorator = decorator; function decorator(name, func) { /*jshint validthis: true */ @@ -1097,8 +1451,85 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ return this; } - // .state(state) - // .state(name, state) + /** + * @ngdoc function + * @name ui.router.state.$stateProvider#state + * @methodOf ui.router.state.$stateProvider + * + * @description + * Registers a state configuration under a given state name. The stateConfig object + * has the following acceptable properties. + * + * - [`template`, `templateUrl`, `templateProvider`] - There are three ways to setup + * your templates. + * + * - `{string|object}` - template - String HTML content, or function that returns an HTML + * string. + * - `{string}` - templateUrl - String URL path to template file OR function, + * that returns URL path string. + * - `{object}` - templateProvider - Provider function that returns HTML content + * string. + * + * - [`controller`, `controllerProvider`] - A controller paired to the state. You can + * either use a controller, or a controller provider. + * + * - `{string|object}` - controller - Controller function or controller name. + * - `{object}` - controllerProvider - Injectable provider function that returns + * the actual controller or string. + * + * - `{object}` - resolve - A map of dependencies which should be injected into the + * controller. + * + * - `{string}` - url - A url with optional parameters. When a state is navigated or + * transitioned to, the `$stateParams` service will be populated with any + * parameters that were passed. + * + * - `{object}` - params - An array of parameter names or regular expressions. Only + * use this within a state if you are not using url. Otherwise you can specify your + * parameters within the url. When a state is navigated or transitioned to, the + * $stateParams service will be populated with any parameters that were passed. + * + * - `{object}` - views - Use the views property to set up multiple views. + * If you don't need multiple views within a single state this property is not + * needed. Tip: remember that often nested views are more useful and powerful + * than multiple sibling views. + * + * - `{boolean}` - abstract - An abstract state will never be directly activated, + * but can provide inherited properties to its common children states. + * + * - `{object}` - onEnter - Callback function for when a state is entered. Good way + * to trigger an action or dispatch an event, such as opening a dialog. + * + * - `{object}` - onExit - Callback function for when a state is exited. Good way to + * trigger an action or dispatch an event, such as opening a dialog. + * + * - `{object}` - data - Arbitrary data object, useful for custom configuration. + * + * @example + *
+   * // The state() method takes a unique stateName (String) and a stateConfig (Object)
+   * $stateProvider.state(stateName, stateConfig);
+   *
+   * // stateName can be a single top-level name (must be unique).
+   * $stateProvider.state("home", {});
+   *
+   * // Or it can be a nested state name. This state is a child of the above "home" state.
+   * $stateProvider.state("home.newest", {});
+   *
+   * // Nest states as deeply as needed.
+   * $stateProvider.state("home.newest.abc.xyz.inception", {});
+   *
+   * // state() returns $stateProvider, so you can chain state declarations.
+   * $stateProvider
+   *   .state("home", {})
+   *   .state("about", {})
+   *   .state("contacts", {});
+   * 
+ * + * @param {string} name A unique state name, e.g. "home", "about", "contacts". + * To create a parent/child state use a dot, e.g. "about.sales", "home.newest". + * @param {object} definition State configuratino object. + */ this.state = state; function state(name, definition) { /*jshint validthis: true */ @@ -1108,6 +1539,29 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ return this; } + /** + * @ngdoc object + * @name ui.router.state.$state + * + * @requires $rootScope + * @requires $q + * @requires ui.router.state.$view + * @requires $injector + * @requires ui.router.util.$resolve + * @requires ui.router.state.$stateParams + * + * @property {object} params A param object, e.g. {sectionId: section.id)}, that + * you'd like to test against the current active state. + * @property {object} current A reference to the state's config object. However + * you passed it in. Useful for accessing custom data. + * @property {object} transition Currently pending transition. A promise that'll + * resolve or reject. + * + * @description + * `$state` service is responsible for representing states as well as transitioning + * between them. It also provides interfaces to ask for current state or even states + * you're coming from. + */ // $urlRouter is injected just to ensure it gets instantiated this.$get = $get; $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$location', '$urlRouter']; @@ -1134,14 +1588,92 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ transition: null }; + /** + * @ngdoc function + * @name ui.router.state.$state#reload + * @methodOf ui.router.state.$state + * + * @description + * Reloads the current state by re-transitioning to it. + * + * @example + *
+     * var app angular.module('app', ['ui.router.state']);
+     *
+     * app.controller('ctrl', function ($state) {
+     *   $state.reload();
+     * });
+     * 
+ */ $state.reload = function reload() { $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: false }); }; + /** + * @ngdoc function + * @name ui.router.state.$state#go + * @methodOf ui.router.state.$state + * + * @description + * Convenience method for transitioning to a new state. `$state.go` calls + * `$state.transitionTo` internally but automatically sets options to + * `{ location: true, inherit: true, relative: $state.$current, notify: true }`. + * This allows you to easily use an absolute or relative to path and specify + * only the parameters you'd like to update (while letting unspecified parameters + * inherit from the current state. + * + * Some examples: + * + * - `$state.go('contact.detail')` - will go to the `contact.detail` state + * - `$state.go('^')` - will go to a parent state + * - `$state.go('^.sibling')` - will go to a sibling state + * - `$state.go('.child.grandchild')` - will go to grandchild state + * + * @example + *
+     * var app = angular.module('app', ['ui.router.state']);
+     *
+     * app.controller('ctrl', function ($scope, $state) {
+     *   $scope.changeState = function () {
+     *     $state.go('contact.detail');
+     *   };
+     * });
+     * 
+ * + * @param {string} to Absolute State Name or Relative State Path. + * @param {object} params A map of the parameters that will be sent to the state, + * will populate $stateParams. + * @param {object} options If Object is passed, object is an options hash. + */ $state.go = function go(to, params, options) { return this.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options)); }; + /** + * @ngdoc function + * @name ui.router.state.$state#transitionTo + * @methodOf ui.router.state.$state + * + * @description + * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go} + * uses `transitionTo` internally. `$state.go` is recommended in most situations. + * + * @example + *
+     * var app = angular.module('app', ['ui.router.state']);
+     *
+     * app.controller('ctrl', function ($scope, $state) {
+     *   $scope.changeState = function () {
+     *     $state.transitionTo('contact.detail');
+     *   };
+     * });
+     * 
+ * + * @param {string} to Absolute State Name or Relative State Path. + * @param {object} params A map of the parameters that will be sent to the state, + * will populate $stateParams. + * @param {object} options If Object is passed, object is an options hash. + */ $state.transitionTo = function transitionTo(to, toParams, options) { toParams = toParams || {}; options = extend({ @@ -1305,6 +1837,30 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ return transition; }; + /** + * @ngdoc function + * @name ui.router.state.$state#is + * @methodOf ui.router.state.$state + * + * @description + * Similar to {@link ui.router.state.$state#methods_includes $state.includes}, + * but only checks for the full state name. If params is supplied then it will be + * tested for strict equality against the current active params object, so all params + * must match with none missing and no extras. + * + * @example + *
+     * $state.is('contact.details.item'); // returns true
+     * $state.is(contactDetailItemStateObject); // returns true
+     *
+     * // everything else would return false
+     * 
+ * + * @param {string|object} stateName The state name or state object you'd like to check. + * @param {object} params A param object, e.g. `{sectionId: section.id}`, that you'd like + * to test against the current active state. + * @returns {boolean} Returns true or false whether its the state or not. + */ $state.is = function is(stateOrName, params) { var state = findState(stateOrName); @@ -1316,9 +1872,33 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ return false; } - return isDefined(params) ? angular.equals($stateParams, params) : true; + return isDefined(params) && params !== null ? angular.equals($stateParams, params) : true; }; + /** + * @ngdoc function + * @name ui.router.state.$state#includes + * @methodOf ui.router.state.$state + * + * @description + * A method to determine if the current active state is equal to or is the child of the + * state stateName. If any params are passed then they will be tested for a match as well. + * Not all the parameters need to be passed, just the ones you'd like to test for equality. + * + * @example + *
+     * $state.includes("contacts"); // returns true
+     * $state.includes("contacts.details"); // returns true
+     * $state.includes("contacts.details.item"); // returns true
+     * $state.includes("contacts.list"); // returns false
+     * $state.includes("about"); // returns false
+     * 
+ * + * @param {string} stateOrName A partial name to be searched for within the current state name. + * @param {object} params A param object, e.g. `{sectionId: section.id}`, + * that you'd like to test against the current active state. + * @returns {boolean} True or false + */ $state.includes = function includes(stateOrName, params) { var state = findState(stateOrName); if (!isDefined(state)) { @@ -1338,6 +1918,23 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ return validParams; }; + /** + * @ngdoc function + * @name ui.router.state.$state#href + * @methodOf ui.router.state.$state + * + * @description + * A url generation method that returns the compiled url for the given state populated with the given params. + * + * @example + *
+     * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
+     * 
+ * + * @param {string|object} stateOrName The state name or state object you'd like to generate a url from. + * @param {object} params An object of parameter values to fill the state's required parameters. + * @returns {string} url + */ $state.href = function href(stateOrName, params, options) { options = extend({ lossy: true, inherit: false, absolute: false, relative: $state.$current }, options || {}); var state = findState(stateOrName, options.relative); @@ -1359,6 +1956,20 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ return url; }; + /** + * @ngdoc function + * @name ui.router.state.$state#get + * @methodOf ui.router.state.$state + * + * @description + * Returns the state configuration object for any state by passing the name + * as a string. Without any arguments it'll return a array of all configured + * state objects. + * + * @param {string|object} stateOrName The name of the state for which you'd like + * to get the original state configuration object for. + * @returns {object} State configuration object or array of all objects. + */ $state.get = function (stateOrName, context) { if (!isDefined(stateOrName)) { var list = []; @@ -1433,10 +2044,30 @@ $ViewProvider.$inject = []; function $ViewProvider() { this.$get = $get; + /** + * @ngdoc object + * @name ui.router.state.$view + * + * @requires ui.router.util.$templateFactory + * @requires $rootScope + * + * @description + * + */ $get.$inject = ['$rootScope', '$templateFactory']; function $get( $rootScope, $templateFactory) { return { // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... }) + /** + * @ngdoc function + * @name ui.router.state.$view#load + * @methodOf ui.router.state.$view + * + * @description + * + * @param {string} name name + * @param {object} options option object. + */ load: function load(name, options) { var result, defaults = { template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {} @@ -1457,61 +2088,136 @@ function $ViewProvider() { angular.module('ui.router.state').provider('$view', $ViewProvider); +/** + * @ngdoc object + * @name ui.router.state.$uiViewScroll + * + * @requires $anchorScroll + * @requires $timeout + * + * @description + * When called with a jqLite element, it scrolls the element into view (after a + * `$timeout` so the DOM has time to refresh). + * + * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor, + * this can be enabled by calling `$uiViewScrollProvider.useAnchorScroll()`. + */ +function $ViewScrollProvider() { -$ViewDirective.$inject = ['$state', '$compile', '$controller', '$injector', '$anchorScroll']; -function $ViewDirective( $state, $compile, $controller, $injector, $anchorScroll) { - var $animator = $injector.has('$animator') ? $injector.get('$animator') : false; - var viewIsUpdating = false; + var useAnchorScroll = false; + + this.useAnchorScroll = function () { + useAnchorScroll = true; + }; + + this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) { + if (useAnchorScroll) { + return $anchorScroll; + } + + return function ($element) { + $timeout(function () { + $element[0].scrollIntoView(); + }, 0, false); + }; + }]; +} + +angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider); + +/** + * @ngdoc directive + * @name ui.router.state.diretive.ui-view + * + * @requires ui.router.state.$state + * @requires $compile + * @requires $controller + * @requires $injector + * + * @restrict ECA + * + * @description + * The ui-view directive tells $state where to place your templates. + * A view can be unnamed or named. + * + * @param {string} ui-view A view name. + */ +$ViewDirective.$inject = ['$state', '$compile', '$controller', '$injector', '$uiViewScroll', '$document']; +function $ViewDirective( $state, $compile, $controller, $injector, $uiViewScroll, $document) { + + function getService() { + return ($injector.has) ? function(service) { + return $injector.has(service) ? $injector.get(service) : null; + } : function(service) { + try { + return $injector.get(service); + } catch (e) { + return null; + } + }; + } + + var viewIsUpdating = false, + service = getService(), + $animator = service('$animator'), + $animate = service('$animate'); + + // Returns a set of DOM manipulation functions based on whether animation + // should be performed + function getRenderer(element, attrs, scope) { + var statics = function() { + return { + leave: function (element) { element.remove(); }, + enter: function (element, parent, anchor) { anchor.after(element); } + }; + }; + + if ($animate) { + return function(shouldAnimate) { + return !shouldAnimate ? statics() : { + enter: function(element, parent, anchor) { $animate.enter(element, null, anchor); }, + leave: function(element) { $animate.leave(element, function() { element.remove(); }); } + }; + }; + } + + if ($animator) { + var animate = $animator && $animator(scope, attrs); + + return function(shouldAnimate) { + return !shouldAnimate ? statics() : { + enter: function(element, parent, anchor) { animate.enter(element, parent); }, + leave: function(element) { animate.leave(element.contents(), element); } + }; + }; + } + + return statics; + } var directive = { restrict: 'ECA', - terminal: true, - priority: 1000, - transclude: true, - compile: function (element, attr, transclude) { - return function(scope, element, attr) { - var viewScope, viewLocals, - name = attr[directive.name] || attr.name || '', - onloadExp = attr.onload || '', - animate = $animator && $animator(scope, attr), - initialView = transclude(scope); + compile: function (element, attrs) { + var initial = element.html(), + isDefault = true, + anchor = angular.element($document[0].createComment(' ui-view-anchor ')), + parentEl = element.parent(); - // Returns a set of DOM manipulation functions based on whether animation - // should be performed - var renderer = function(doAnimate) { - return ({ - "true": { - remove: function(element) { animate.leave(element.contents(), element); }, - restore: function(compiled, element) { animate.enter(compiled, element); }, - populate: function(template, element) { - var contents = angular.element('
').html(template).contents(); - animate.enter(contents, element); - return contents; - } - }, - "false": { - remove: function(element) { element.html(''); }, - restore: function(compiled, element) { element.append(compiled); }, - populate: function(template, element) { - element.html(template); - return element.contents(); - } - } - })[doAnimate.toString()]; - }; + element.prepend(anchor); - // Put back the compiled initial view - element.append(initialView); + return function ($scope) { + var inherited = parentEl.inheritedData('$uiView'); - // Find the details of the parent view directive (if any) and use it - // to derive our own qualified view name, then hang our own details - // off the DOM so child directives can find it. - var parent = element.parent().inheritedData('$uiView'); - if (name.indexOf('@') < 0) name = name + '@' + (parent ? parent.state.name : ''); + var currentScope, currentEl, viewLocals, + name = attrs[directive.name] || attrs.name || '', + onloadExp = attrs.onload || '', + autoscrollExp = attrs.autoscroll, + renderer = getRenderer(element, attrs, $scope); + + if (name.indexOf('@') < 0) name = name + '@' + (inherited ? inherited.state.name : ''); var view = { name: name, state: null }; - element.data('$uiView', view); - var eventHook = function() { + var eventHook = function () { if (viewIsUpdating) return; viewIsUpdating = true; @@ -1522,54 +2228,78 @@ function $ViewDirective( $state, $compile, $controller, $injector, $an viewIsUpdating = false; }; - scope.$on('$stateChangeSuccess', eventHook); - scope.$on('$viewContentLoading', eventHook); + $scope.$on('$stateChangeSuccess', eventHook); + $scope.$on('$viewContentLoading', eventHook); + updateView(false); - function updateView(doAnimate) { + function cleanupLastView() { + if (currentEl) { + renderer(true).leave(currentEl); + currentEl = null; + } + + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + } + + function updateView(shouldAnimate) { var locals = $state.$current && $state.$current.locals[name]; - if (locals === viewLocals) return; // nothing to do - var render = renderer(animate && doAnimate); - // Remove existing content - render.remove(element); - - // Destroy previous view scope - if (viewScope) { - viewScope.$destroy(); - viewScope = null; + if (isDefault) { + isDefault = false; + element.replaceWith(anchor); } if (!locals) { - viewLocals = null; - view.state = null; + cleanupLastView(); + currentEl = element.clone(); + currentEl.html(initial); + renderer(shouldAnimate).enter(currentEl, parentEl, anchor); - // Restore the initial view - return render.restore(initialView, element); + currentScope = $scope.$new(); + $compile(currentEl.contents())(currentScope); + return; } + if (locals === viewLocals) return; // nothing to do + + cleanupLastView(); + + currentEl = element.clone(); + currentEl.html(locals.$template ? locals.$template : initial); + renderer(true).enter(currentEl, parentEl, anchor); + + currentEl.data('$uiView', view); + viewLocals = locals; view.state = locals.$$state; - var link = $compile(render.populate(locals.$template, element)); - viewScope = scope.$new(); + var link = $compile(currentEl.contents()); + + currentScope = $scope.$new(); if (locals.$$controller) { - locals.$scope = viewScope; + locals.$scope = currentScope; var controller = $controller(locals.$$controller, locals); - element.children().data('$ngControllerController', controller); + currentEl.children().data('$ngControllerController', controller); } - link(viewScope); - viewScope.$emit('$viewContentLoaded'); - if (onloadExp) viewScope.$eval(onloadExp); - // TODO: This seems strange, shouldn't $anchorScroll listen for $viewContentLoaded if necessary? - // $anchorScroll might listen on event... - $anchorScroll(); + link(currentScope); + + currentScope.$emit('$viewContentLoaded'); + if (onloadExp) currentScope.$eval(onloadExp); + + if (!angular.isDefined(autoscrollExp) || !autoscrollExp || $scope.$eval(autoscrollExp)) { + $uiViewScroll(currentEl); + } } }; } }; + return directive; } @@ -1589,6 +2319,42 @@ function stateContext(el) { } } +/** + * @ngdoc directive + * @name ui.router.state.directive:ui-sref + * + * @requires ui.router.state.$state + * @requires $timeout + * + * @restrict A + * + * @description + * A directive that binds a link (`` tag) to a state. If the state has an associated + * URL, the directive will automatically generate & update the `href` attribute via + * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking + * the link will trigger a state transition with optional parameters. + * + * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be + * handled natively by the browser. + * + * You can also use relative state paths within ui-sref, just like the relative + * paths passed to `$state.go()`. You just need to be aware that the path is relative + * to the state that the link lives in, in other words the state that loaded the + * template containing the link. + * + * @example + *
+ * Home | About
+ *
+ * 
+ * 
+ * + * @param {string} ui-sref 'stateName' can be any valid absolute or relative state + */ $StateRefDirective.$inject = ['$state', '$timeout']; function $StateRefDirective($state, $timeout) { return { @@ -1606,14 +2372,14 @@ function $StateRefDirective($state, $timeout) { var newHref = $state.href(ref.state, params, { relative: base }); + if (uiSrefActive) { + uiSrefActive.$$setStateInfo(ref.state, params); + } if (!newHref) { nav = false; return false; } element[0][attr] = newHref; - if (uiSrefActive) { - uiSrefActive.$$setStateInfo(ref.state, params); - } }; if (ref.paramExpr) { @@ -1628,13 +2394,10 @@ function $StateRefDirective($state, $timeout) { element.bind("click", function(e) { var button = e.which || e.button; - - if ((button === 0 || button == 1) && !e.ctrlKey && !e.metaKey && !e.shiftKey) { + if ((button === 0 || button == 1) && !e.ctrlKey && !e.metaKey && !e.shiftKey && !element.attr('target')) { // HACK: This is to allow ng-clicks to be processed before the transition is initiated: $timeout(function() { - scope.$apply(function() { - $state.go(ref.state, params, { relative: base }); - }); + $state.go(ref.state, params, { relative: base }); }); e.preventDefault(); } @@ -1643,11 +2406,38 @@ function $StateRefDirective($state, $timeout) { }; } +/** + * @ngdoc directive + * @name ui.router.state.directive:ui-sref-active + * + * @requires ui.router.state.$state + * @requires ui.router.state.$stateParams + * @requires $interpolate + * + * @restrict A + * + * @description + * A directive working alongside ui-sref to add classes to an element when the + * related ui-sref directive's state is active, and removing them when it is inactive. + * The primary use-case is to simplify the special appearance of navigation menus + * relying on `ui-sref`, by having the "active" state's menu button appear different, + * distinguishing it from the inactive menu items. + * + * @example + *
+ * 
+ * 
+ */ $StateActiveDirective.$inject = ['$state', '$stateParams', '$interpolate']; function $StateActiveDirective($state, $stateParams, $interpolate) { return { restrict: "A", - controller: function($scope, $element, $attrs) { + controller: ['$scope', '$element', '$attrs', function($scope, $element, $attrs) { var state, params, activeClass; // There probably isn't much point in $observing this @@ -1674,7 +2464,7 @@ function $StateActiveDirective($state, $stateParams, $interpolate) { function matchesParams() { return !params || equalForKeys(params, $stateParams); } - } + }] }; } @@ -1682,6 +2472,60 @@ angular.module('ui.router.state') .directive('uiSref', $StateRefDirective) .directive('uiSrefActive', $StateActiveDirective); +/** + * @ngdoc filter + * @name ui.router.state.filter:isState + * + * @requires ui.router.state.$state + * + * @description + * Translates to {@link ui.router.state.$state#is $state.is("stateName")}. + */ +$IsStateFilter.$inject = ['$state']; +function $IsStateFilter($state) { + return function(state) { + return $state.is(state); + }; +} + +/** + * @ngdoc filter + * @name ui.router.state.filter:includeByState + * + * @requires ui.router.state.$state + * + * @description + * Translates to {@link ui.router.state.$state#includes $state.includes()}. + */ +$IncludedByStateFilter.$inject = ['$state']; +function $IncludedByStateFilter($state) { + return function(state) { + return $state.includes(state); + }; +} + +angular.module('ui.router.state') + .filter('isState', $IsStateFilter) + .filter('includedByState', $IncludedByStateFilter); + +/** + * @ngdoc object + * @name ui.router.compat.$routeProvider + * + * @requires ui.router.state.$stateProvider + * @requires ui.router.router.$urlRouterProvider + * + * @description + * `$routeProvider` of the `ui.router.compat` module overwrites the existing + * `routeProvider` from the core. This is done to provide compatibility between + * the UI Router and the core router. + * + * It also provides a `when()` method to register routes that map to certain urls. + * Behind the scenes it actually delegates either to + * {@link ui.router.router.$urlRouterProvider $urlRouterProvider} or to the + * {@link ui.router.state.$stateProvider $stateProvider} to postprocess the given + * router definition object. + */ $RouteProvider.$inject = ['$stateProvider', '$urlRouterProvider']; function $RouteProvider( $stateProvider, $urlRouterProvider) { @@ -1701,6 +2545,32 @@ function $RouteProvider( $stateProvider, $urlRouterProvider) { } this.when = when; + /** + * @ngdoc function + * @name ui.router.compat.$routeProvider#when + * @methodOf ui.router.compat.$routeProvider + * + * @description + * Registers a route with a given route definition object. The route definition + * object has the same interface the angular core route definition object has. + * + * @example + *
+   * var app = angular.module('app', ['ui.router.compat']);
+   *
+   * app.config(function ($routeProvider) {
+   *   $routeProvider.when('home', {
+   *     controller: function () { ... },
+   *     templateUrl: 'path/to/template'
+   *   });
+   * });
+   * 
+ * + * @param {string} url URL as string + * @param {object} route Route definition object + * + * @return {object} $routeProvider - $routeProvider instance + */ function when(url, route) { /*jshint validthis: true */ if (route.redirectTo != null) { @@ -1731,6 +2601,24 @@ function $RouteProvider( $stateProvider, $urlRouterProvider) { return this; } + /** + * @ngdoc object + * @name ui.router.compat.$route + * + * @requires ui.router.state.$state + * @requires $rootScope + * @requires $routeParams + * + * @property {object} routes - Array of registered routes. + * @property {object} params - Current route params as object. + * @property {string} current - Name of the current route. + * + * @description + * The `$route` service provides interfaces to access defined routes. It also let's + * you access route params through `$routeParams` service, so you have fully + * control over all the stuff you would actually get from angular's core `$route` + * service. + */ this.$get = $get; $get.$inject = ['$state', '$rootScope', '$routeParams']; function $get( $state, $rootScope, $routeParams) { diff --git a/dist/js/angular-ui/angular-ui-router.min.js b/dist/js/angular-ui/angular-ui-router.min.js index fece6d9b91..b3e0d9bb97 100755 --- a/dist/js/angular-ui/angular-ui-router.min.js +++ b/dist/js/angular-ui/angular-ui-router.min.js @@ -1,7 +1,7 @@ /** * State-based routing for AngularJS - * @version v0.2.7 + * @version v0.2.8 * @link http://angular-ui.github.com/ * @license MIT License, http://www.opensource.org/licenses/MIT */ -"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ui.router"),function(a,b,c){"use strict";function d(a,b){return E(new(E(function(){},{prototype:a})),b)}function e(a){return D(arguments,function(b){b!==a&&D(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]){if(!b.path[d])break;c.push(a.path[d])}return c}function g(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 h(a,b,c,d){var e,h=f(c,d),i={},j=[];for(var k in h)if(h[k].params&&h[k].params.length){e=h[k].params;for(var l in e)g(j,e[l])>=0||(j.push(e[l]),i[e[l]]=a[e[l]])}return E({},i,b)}function i(a,b){var c={};return D(a,function(a){var d=b[a];c[a]=null!=d?String(d):null}),c}function j(a,b,c){if(!c){c=[];for(var d in a)c.push(d)}for(var e=0;e "));if(o[c]=d,A(a))m.push(c,[function(){return b.get(a)}],h);else{var e=b.annotate(a);D(e,function(a){a!==c&&g.hasOwnProperty(a)&&k(g[a],a)}),m.push(c,a,e)}n.pop(),o[c]=f}}function l(a){return B(a)&&a.then&&a.$$promises}if(!B(g))throw new Error("'invocables' must be an object");var m=[],n=[],o={};return D(g,k),g=n=o=null,function(d,f,g){function h(){--s||(t||e(r,f.$$values),p.$$values=r,p.$$promises=!0,o.resolve(r))}function k(a){p.$$failure=a,o.reject(a)}function n(c,e,f){function i(a){l.reject(a),k(a)}function j(){if(!y(p.$$failure))try{l.resolve(b.invoke(e,g,r)),l.promise.then(function(a){r[c]=a,h()},i)}catch(a){i(a)}}var l=a.defer(),m=0;D(f,function(a){q.hasOwnProperty(a)&&!d.hasOwnProperty(a)&&(m++,q[a].then(function(b){r[a]=b,--m||j()},i))}),m||j(),q[c]=l.promise}if(l(d)&&g===c&&(g=f,f=d,d=null),d){if(!B(d))throw new Error("'locals' must be an object")}else d=i;if(f){if(!l(f))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else f=j;var o=a.defer(),p=o.promise,q=p.$$promises={},r=E({},d),s=1+m.length/3,t=!1;if(y(f.$$failure))return k(f.$$failure),p;f.$$values?(t=e(r,f.$$values),h()):(E(q,f.$$promises),f.then(h,k));for(var u=0,v=m.length;v>u;u+=3)d.hasOwnProperty(m[u])?h():n(m[u],m[u+1],m[u+2]);return p}},this.resolve=function(a,b,c,d){return this.study(a)(b,c,d)}}function m(a,b,c){this.fromConfig=function(a,b,c){return y(a.template)?this.fromString(a.template,b):y(a.templateUrl)?this.fromUrl(a.templateUrl,b):y(a.templateProvider)?this.fromProvider(a.templateProvider,b,c):null},this.fromString=function(a,b){return z(a)?a(b):a},this.fromUrl=function(c,d){return z(c)&&(c=c(d)),null==c?null:a.get(c,{cache:b}).then(function(a){return a.data})},this.fromProvider=function(a,b,d){return c.invoke(a,null,d||{params:b})}}function n(a){function b(b){if(!/^\w+(-+\w+)*$/.test(b))throw new Error("Invalid parameter name '"+b+"' in pattern '"+a+"'");if(f[b])throw new Error("Duplicate parameter name '"+b+"' in pattern '"+a+"'");f[b]=!0,j.push(b)}function c(a){return a.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&")}var d,e=/([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,f={},g="^",h=0,i=this.segments=[],j=this.params=[];this.source=a;for(var k,l,m;(d=e.exec(a))&&(k=d[2]||d[3],l=d[4]||("*"==d[1]?".*":"[^/]*"),m=a.substring(h,d.index),!(m.indexOf("?")>=0));)g+=c(m)+"("+l+")",b(k),i.push(m),h=e.lastIndex;m=a.substring(h);var n=m.indexOf("?");if(n>=0){var o=this.sourceSearch=m.substring(n);m=m.substring(0,n),this.sourcePath=a.substring(0,h+n),D(o.substring(1).split(/[&?]/),b)}else this.sourcePath=a,this.sourceSearch="";g+=c(m)+"$",i.push(m),this.regexp=new RegExp(g),this.prefix=i[0]}function o(){this.compile=function(a){return new n(a)},this.isMatcher=function(a){return B(a)&&z(a.exec)&&z(a.format)&&z(a.concat)},this.$get=function(){return this}}function p(a){function b(a){var b=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(a.source);return null!=b?b[1].replace(/\\(.)/g,"$1"):""}function c(a,b){return a.replace(/\$(\$|\d{1,2})/,function(a,c){return b["$"===c?0:Number(c)]})}function d(a,b,c){if(!c)return!1;var d=a.invoke(b,b,{$match:c});return y(d)?d:!0}var e=[],f=null;this.rule=function(a){if(!z(a))throw new Error("'rule' must be a function");return e.push(a),this},this.otherwise=function(a){if(A(a)){var b=a;a=function(){return b}}else if(!z(a))throw new Error("'rule' must be a function");return f=a,this},this.when=function(e,f){var g,h=A(f);if(A(e)&&(e=a.compile(e)),!h&&!z(f)&&!C(f))throw new Error("invalid 'handler' in when()");var i={matcher:function(b,c){return h&&(g=a.compile(c),c=["$match",function(a){return g.format(a)}]),E(function(a,e){return d(a,c,b.exec(e.path(),e.search()))},{prefix:A(b.prefix)?b.prefix:""})},regex:function(a,e){if(a.global||a.sticky)throw new Error("when() RegExp must not be global or sticky");return h&&(g=e,e=["$match",function(a){return c(g,a)}]),E(function(b,c){return d(b,e,a.exec(c.path()))},{prefix:b(a)})}},j={matcher:a.isMatcher(e),regex:e instanceof RegExp};for(var k in j)if(j[k])return this.rule(i[k](e,f));throw new Error("invalid 'what' in when()")},this.$get=["$location","$rootScope","$injector",function(a,b,c){function d(b){function d(b){var d=b(c,a);return d?(A(d)&&a.replace().url(d),!0):!1}if(!b||!b.defaultPrevented){var g,h=e.length;for(g=0;h>g;g++)if(d(e[g]))return;f&&d(f)}}return b.$on("$locationChangeSuccess",d),{sync:function(){d()}}}]}function q(a,e,f){function g(a){return 0===a.indexOf(".")||0===a.indexOf("^")}function l(a,b){var d=A(a),e=d?a:a.name,f=g(e);if(f){if(!b)throw new Error("No reference point given for path '"+e+"'");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=u[e];return!l||!d&&(d||l!==a&&l.self!==a)?c:l}function m(a,b){v[a]||(v[a]=[]),v[a].push(b)}function n(b){b=d(b,{self:b,resolve:b.resolve||{},toString:function(){return this.name}});var c=b.name;if(!A(c)||c.indexOf("@")>=0)throw new Error("State must have a valid name");if(u.hasOwnProperty(c))throw new Error("State '"+c+"'' is already defined");var e=-1!==c.indexOf(".")?c.substring(0,c.lastIndexOf(".")):A(b.parent)?b.parent:"";if(e&&!u[e])return m(e,b.self);for(var f in x)z(x[f])&&(b[f]=x[f](b,x.$delegates[f]));if(u[c]=b,!b[w]&&b.url&&a.when(b.url,["$match","$stateParams",function(a,c){t.$current.navigable==b&&j(a,c)||t.transitionTo(b,a,{location:!1})}]),v[c])for(var g=0;g=I;d--)g=u[d],g.self.onExit&&m.invoke(g.self.onExit,g.self,g.locals.globals),g.locals=null;for(d=I;d").html(a).contents();return r.enter(d,c),d}},"false":{remove:function(a){a.html("")},restore:function(a,b){b.append(a)},populate:function(a,b){return b.html(a),b.contents()}}}[a.toString()]};j.append(s);var u=j.parent().inheritedData("$uiView");p.indexOf("@")<0&&(p=p+"@"+(u?u.state.name:""));var v={name:p,state:null};j.data("$uiView",v);var w=function(){if(!h){h=!0;try{m(!0)}catch(a){throw h=!1,a}h=!1}};e.$on("$stateChangeSuccess",w),e.$on("$viewContentLoading",w),m(!1)}}};return i}function t(a){var b=a.replace(/\n/g," ").match(/^([^(]+?)\s*(\((.*)\))?$/);if(!b||4!==b.length)throw new Error("Invalid state ref '"+a+"'");return{state:b[1],paramExpr:b[3]||null}}function u(a){var b=a.parent().inheritedData("$uiView");return b&&b.state&&b.state.name?b.state:void 0}function v(a,b){return{restrict:"A",require:"?^uiSrefActive",link:function(c,d,e,f){var g=t(e.uiSref),h=null,i=u(d)||a.$current,j="FORM"===d[0].nodeName,k=j?"action":"href",l=!0,m=function(b){if(b&&(h=b),l){var c=a.href(g.state,h,{relative:i});if(!c)return l=!1,!1;d[0][k]=c,f&&f.$$setStateInfo(g.state,h)}};g.paramExpr&&(c.$watch(g.paramExpr,function(a){a!==h&&m(a)},!0),h=c.$eval(g.paramExpr)),m(),j||d.bind("click",function(d){var e=d.which||d.button;0!==e&&1!=e||d.ctrlKey||d.metaKey||d.shiftKey||(b(function(){c.$apply(function(){a.go(g.state,h,{relative:i})})}),d.preventDefault())})}}}function w(a,b,c){return{restrict:"A",controller:function(d,e,f){function g(){a.$current.self===i&&h()?e.addClass(l):e.removeClass(l)}function h(){return!k||j(k,b)}var i,k,l;l=c(f.uiSrefActive||"",!1)(d),this.$$setStateInfo=function(b,c){i=a.get(b,u(e)),k=c,g()},d.$on("$stateChangeSuccess",g)}}}function x(a,b){function e(a){this.locals=a.locals.globals,this.params=this.locals.$stateParams}function f(){this.locals=null,this.params=null}function g(c,g){if(null!=g.redirectTo){var h,j=g.redirectTo;if(A(j))h=j;else{if(!z(j))throw new Error("Invalid 'redirectTo' in when()");h=function(a,b){return j(a,b.path(),b.search())}}b.when(c,h)}else a.state(d(g,{parent:null,name:"route:"+encodeURIComponent(c),url:c,onEnter:e,onExit:f}));return i.push(g),this}function h(a,b,d){function e(a){return""!==a.name?a:c}var f={routes:i,params:d,current:c};return b.$on("$stateChangeStart",function(a,c,d,f){b.$broadcast("$routeChangeStart",e(c),e(f))}),b.$on("$stateChangeSuccess",function(a,c,d,g){f.current=e(c),b.$broadcast("$routeChangeSuccess",e(c),e(g)),F(d,f.params)}),b.$on("$stateChangeError",function(a,c,d,f,g,h){b.$broadcast("$routeChangeError",e(c),e(f),h)}),f}var i=[];e.$inject=["$$state"],this.when=g,this.$get=h,h.$inject=["$state","$rootScope","$routeParams"]}var y=b.isDefined,z=b.isFunction,A=b.isString,B=b.isObject,C=b.isArray,D=b.forEach,E=b.extend,F=b.copy;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"]),l.$inject=["$q","$injector"],b.module("ui.router.util").service("$resolve",l),m.$inject=["$http","$templateCache","$injector"],b.module("ui.router.util").service("$templateFactory",m),n.prototype.concat=function(a){return new n(this.sourcePath+a+this.sourceSearch)},n.prototype.toString=function(){return this.source},n.prototype.exec=function(a,b){var c=this.regexp.exec(a);if(!c)return null;var d,e=this.params,f=e.length,g=this.segments.length-1,h={};if(g!==c.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");for(d=0;g>d;d++)h[e[d]]=c[d+1];for(;f>d;d++)h[e[d]]=b[e[d]];return h},n.prototype.parameters=function(){return this.params},n.prototype.format=function(a){var b=this.segments,c=this.params;if(!a)return b.join("");var d,e,f,g=b.length-1,h=c.length,i=b[0];for(d=0;g>d;d++)f=a[c[d]],null!=f&&(i+=encodeURIComponent(f)),i+=b[d+1];for(;h>d;d++)f=a[c[d]],null!=f&&(i+=(e?"&":"?")+c[d]+"="+encodeURIComponent(f),e=!0);return i},b.module("ui.router.util").provider("$urlMatcherFactory",o),p.$inject=["$urlMatcherFactoryProvider"],b.module("ui.router.router").provider("$urlRouter",p),q.$inject=["$urlRouterProvider","$urlMatcherFactoryProvider","$locationProvider"],b.module("ui.router.state").value("$stateParams",{}).provider("$state",q),r.$inject=[],b.module("ui.router.state").provider("$view",r),s.$inject=["$state","$compile","$controller","$injector","$anchorScroll"],b.module("ui.router.state").directive("uiView",s),v.$inject=["$state","$timeout"],w.$inject=["$state","$stateParams","$interpolate"],b.module("ui.router.state").directive("uiSref",v).directive("uiSrefActive",w),x.$inject=["$stateProvider","$urlRouterProvider"],b.module("ui.router.compat").provider("$route",x).directive("ngView",s)}(window,window.angular); \ No newline at end of file +"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ui.router"),function(a,b,c){"use strict";function d(a,b){return H(new(H(function(){},{prototype:a})),b)}function e(a){return G(arguments,function(b){b!==a&&G(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,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 h(a,b,c,d){var e,h=f(c,d),i={},j=[];for(var k in h)if(h[k].params&&h[k].params.length){e=h[k].params;for(var l in e)g(j,e[l])>=0||(j.push(e[l]),i[e[l]]=a[e[l]])}return H({},i,b)}function i(a,b){var c={};return G(a,function(a){var d=b[a];c[a]=null!=d?String(d):null}),c}function j(a,b,c){if(!c){c=[];for(var d in a)c.push(d)}for(var e=0;e "));if(o[c]=d,D(a))m.push(c,[function(){return b.get(a)}],h);else{var e=b.annotate(a);G(e,function(a){a!==c&&g.hasOwnProperty(a)&&k(g[a],a)}),m.push(c,a,e)}n.pop(),o[c]=f}}function l(a){return E(a)&&a.then&&a.$$promises}if(!E(g))throw new Error("'invocables' must be an object");var m=[],n=[],o={};return G(g,k),g=n=o=null,function(d,f,g){function h(){--s||(t||e(r,f.$$values),p.$$values=r,p.$$promises=!0,o.resolve(r))}function k(a){p.$$failure=a,o.reject(a)}function n(c,e,f){function i(a){l.reject(a),k(a)}function j(){if(!B(p.$$failure))try{l.resolve(b.invoke(e,g,r)),l.promise.then(function(a){r[c]=a,h()},i)}catch(a){i(a)}}var l=a.defer(),m=0;G(f,function(a){q.hasOwnProperty(a)&&!d.hasOwnProperty(a)&&(m++,q[a].then(function(b){r[a]=b,--m||j()},i))}),m||j(),q[c]=l.promise}if(l(d)&&g===c&&(g=f,f=d,d=null),d){if(!E(d))throw new Error("'locals' must be an object")}else d=i;if(f){if(!l(f))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else f=j;var o=a.defer(),p=o.promise,q=p.$$promises={},r=H({},d),s=1+m.length/3,t=!1;if(B(f.$$failure))return k(f.$$failure),p;f.$$values?(t=e(r,f.$$values),h()):(H(q,f.$$promises),f.then(h,k));for(var u=0,v=m.length;v>u;u+=3)d.hasOwnProperty(m[u])?h():n(m[u],m[u+1],m[u+2]);return p}},this.resolve=function(a,b,c,d){return this.study(a)(b,c,d)}}function m(a,b,c){this.fromConfig=function(a,b,c){return B(a.template)?this.fromString(a.template,b):B(a.templateUrl)?this.fromUrl(a.templateUrl,b):B(a.templateProvider)?this.fromProvider(a.templateProvider,b,c):null},this.fromString=function(a,b){return C(a)?a(b):a},this.fromUrl=function(c,d){return C(c)&&(c=c(d)),null==c?null:a.get(c,{cache:b}).then(function(a){return a.data})},this.fromProvider=function(a,b,d){return c.invoke(a,null,d||{params:b})}}function n(a){function b(b){if(!/^\w+(-+\w+)*$/.test(b))throw new Error("Invalid parameter name '"+b+"' in pattern '"+a+"'");if(f[b])throw new Error("Duplicate parameter name '"+b+"' in pattern '"+a+"'");f[b]=!0,j.push(b)}function c(a){return a.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&")}var d,e=/([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,f={},g="^",h=0,i=this.segments=[],j=this.params=[];this.source=a;for(var k,l,m;(d=e.exec(a))&&(k=d[2]||d[3],l=d[4]||("*"==d[1]?".*":"[^/]*"),m=a.substring(h,d.index),!(m.indexOf("?")>=0));)g+=c(m)+"("+l+")",b(k),i.push(m),h=e.lastIndex;m=a.substring(h);var n=m.indexOf("?");if(n>=0){var o=this.sourceSearch=m.substring(n);m=m.substring(0,n),this.sourcePath=a.substring(0,h+n),G(o.substring(1).split(/[&?]/),b)}else this.sourcePath=a,this.sourceSearch="";g+=c(m)+"$",i.push(m),this.regexp=new RegExp(g),this.prefix=i[0]}function o(){this.compile=function(a){return new n(a)},this.isMatcher=function(a){return E(a)&&C(a.exec)&&C(a.format)&&C(a.concat)},this.$get=function(){return this}}function p(a){function b(a){var b=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(a.source);return null!=b?b[1].replace(/\\(.)/g,"$1"):""}function c(a,b){return a.replace(/\$(\$|\d{1,2})/,function(a,c){return b["$"===c?0:Number(c)]})}function d(a,b,c){if(!c)return!1;var d=a.invoke(b,b,{$match:c});return B(d)?d:!0}var e=[],f=null;this.rule=function(a){if(!C(a))throw new Error("'rule' must be a function");return e.push(a),this},this.otherwise=function(a){if(D(a)){var b=a;a=function(){return b}}else if(!C(a))throw new Error("'rule' must be a function");return f=a,this},this.when=function(e,f){var g,h=D(f);if(D(e)&&(e=a.compile(e)),!h&&!C(f)&&!F(f))throw new Error("invalid 'handler' in when()");var i={matcher:function(b,c){return h&&(g=a.compile(c),c=["$match",function(a){return g.format(a)}]),H(function(a,e){return d(a,c,b.exec(e.path(),e.search()))},{prefix:D(b.prefix)?b.prefix:""})},regex:function(a,e){if(a.global||a.sticky)throw new Error("when() RegExp must not be global or sticky");return h&&(g=e,e=["$match",function(a){return c(g,a)}]),H(function(b,c){return d(b,e,a.exec(c.path()))},{prefix:b(a)})}},j={matcher:a.isMatcher(e),regex:e instanceof RegExp};for(var k in j)if(j[k])return this.rule(i[k](e,f));throw new Error("invalid 'what' in when()")},this.$get=["$location","$rootScope","$injector",function(a,b,c){function d(b){function d(b){var d=b(c,a);return d?(D(d)&&a.replace().url(d),!0):!1}if(!b||!b.defaultPrevented){var g,h=e.length;for(g=0;h>g;g++)if(d(e[g]))return;f&&d(f)}}return b.$on("$locationChangeSuccess",d),{sync:function(){d()}}}]}function q(a,e,f){function g(a){return 0===a.indexOf(".")||0===a.indexOf("^")}function l(a,b){var d=D(a),e=d?a:a.name,f=g(e);if(f){if(!b)throw new Error("No reference point given for path '"+e+"'");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=u[e];return!l||!d&&(d||l!==a&&l.self!==a)?c:l}function m(a,b){v[a]||(v[a]=[]),v[a].push(b)}function n(b){b=d(b,{self:b,resolve:b.resolve||{},toString:function(){return this.name}});var c=b.name;if(!D(c)||c.indexOf("@")>=0)throw new Error("State must have a valid name");if(u.hasOwnProperty(c))throw new Error("State '"+c+"'' is already defined");var e=-1!==c.indexOf(".")?c.substring(0,c.lastIndexOf(".")):D(b.parent)?b.parent:"";if(e&&!u[e])return m(e,b.self);for(var f in x)C(x[f])&&(b[f]=x[f](b,x.$delegates[f]));if(u[c]=b,!b[w]&&b.url&&a.when(b.url,["$match","$stateParams",function(a,c){t.$current.navigable==b&&j(a,c)||t.transitionTo(b,a,{location:!1})}]),v[c])for(var g=0;g=G;d--)g=u[d],g.self.onExit&&m.invoke(g.self.onExit,g.self,g.locals.globals),g.locals=null;for(d=G;dd;d++)h[e[d]]=c[d+1];for(;f>d;d++)h[e[d]]=b[e[d]];return h},n.prototype.parameters=function(){return this.params},n.prototype.format=function(a){var b=this.segments,c=this.params;if(!a)return b.join("");var d,e,f,g=b.length-1,h=c.length,i=b[0];for(d=0;g>d;d++)f=a[c[d]],null!=f&&(i+=encodeURIComponent(f)),i+=b[d+1];for(;h>d;d++)f=a[c[d]],null!=f&&(i+=(e?"&":"?")+c[d]+"="+encodeURIComponent(f),e=!0);return i},b.module("ui.router.util").provider("$urlMatcherFactory",o),p.$inject=["$urlMatcherFactoryProvider"],b.module("ui.router.router").provider("$urlRouter",p),q.$inject=["$urlRouterProvider","$urlMatcherFactoryProvider","$locationProvider"],b.module("ui.router.state").value("$stateParams",{}).provider("$state",q),r.$inject=[],b.module("ui.router.state").provider("$view",r),b.module("ui.router.state").provider("$uiViewScroll",s),t.$inject=["$state","$compile","$controller","$injector","$uiViewScroll","$document"],b.module("ui.router.state").directive("uiView",t),w.$inject=["$state","$timeout"],x.$inject=["$state","$stateParams","$interpolate"],b.module("ui.router.state").directive("uiSref",w).directive("uiSrefActive",x),y.$inject=["$state"],z.$inject=["$state"],b.module("ui.router.state").filter("isState",y).filter("includedByState",z),A.$inject=["$stateProvider","$urlRouterProvider"],b.module("ui.router.compat").provider("$route",A).directive("ngView",t)}(window,window.angular); \ No newline at end of file