} 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