| /** |
| * @ngdoc object |
| * @name ui.router.router.$urlRouterProvider |
| * |
| * @requires ui.router.util.$urlMatcherFactoryProvider |
| * @requires $locationProvider |
| * |
| * @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 = ['$locationProvider', '$urlMatcherFactoryProvider']; |
| function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) { |
| var rules = [], otherwise = null, interceptDeferred = false, listener; |
| |
| // Returns a string that is a prefix of all strings matching the RegExp |
| function regExpPrefix(re) { |
| var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source); |
| return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : ''; |
| } |
| |
| // Interpolates matched values into a String.replace()-style pattern |
| function interpolate(pattern, match) { |
| return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) { |
| return match[what === '$' ? 0 : Number(what)]; |
| }); |
| } |
| |
| /** |
| * @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 |
| * <pre> |
| * 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; |
| * } |
| * }); |
| * }); |
| * </pre> |
| * |
| * @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"); |
| rules.push(rule); |
| return this; |
| }; |
| |
| /** |
| * @ngdoc object |
| * @name ui.router.router.$urlRouterProvider#otherwise |
| * @methodOf ui.router.router.$urlRouterProvider |
| * |
| * @description |
| * Defines a path that is used when an invalid route is requested. |
| * |
| * @example |
| * <pre> |
| * 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) { |
| * return '/a/valid/url'; |
| * }); |
| * }); |
| * </pre> |
| * |
| * @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, and must return a url string. |
| * |
| * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance |
| */ |
| this.otherwise = function (rule) { |
| if (isString(rule)) { |
| var redirect = rule; |
| rule = function () { return redirect; }; |
| } |
| else if (!isFunction(rule)) throw new Error("'rule' must be a function"); |
| otherwise = rule; |
| return this; |
| }; |
| |
| |
| function handleIfMatch($injector, handler, match) { |
| if (!match) return false; |
| var result = $injector.invoke(handler, handler, { $match: match }); |
| 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 syntax 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 |
| * <pre> |
| * 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); |
| * } |
| * }); |
| * }); |
| * </pre> |
| * |
| * @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); |
| if (isString(what)) what = $urlMatcherFactory.compile(what); |
| |
| if (!handlerIsString && !isFunction(handler) && !isArray(handler)) |
| throw new Error("invalid 'handler' in when()"); |
| |
| var strategies = { |
| matcher: function (what, handler) { |
| if (handlerIsString) { |
| redirect = $urlMatcherFactory.compile(handler); |
| handler = ['$match', function ($match) { return redirect.format($match); }]; |
| } |
| return extend(function ($injector, $location) { |
| return handleIfMatch($injector, handler, what.exec($location.path(), $location.search())); |
| }, { |
| prefix: isString(what.prefix) ? what.prefix : '' |
| }); |
| }, |
| regex: function (what, handler) { |
| if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky"); |
| |
| if (handlerIsString) { |
| redirect = handler; |
| handler = ['$match', function ($match) { return interpolate(redirect, $match); }]; |
| } |
| return extend(function ($injector, $location) { |
| return handleIfMatch($injector, handler, what.exec($location.path())); |
| }, { |
| prefix: regExpPrefix(what) |
| }); |
| } |
| }; |
| |
| var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp }; |
| |
| for (var n in check) { |
| if (check[n]) return this.rule(strategies[n](what, handler)); |
| } |
| |
| throw new Error("invalid 'what' in when()"); |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name ui.router.router.$urlRouterProvider#deferIntercept |
| * @methodOf ui.router.router.$urlRouterProvider |
| * |
| * @description |
| * Disables (or enables) deferring location change interception. |
| * |
| * If you wish to customize the behavior of syncing the URL (for example, if you wish to |
| * defer a transition but maintain the current URL), call this method at configuration time. |
| * Then, at run time, call `$urlRouter.listen()` after you have configured your own |
| * `$locationChangeSuccess` event handler. |
| * |
| * @example |
| * <pre> |
| * var app = angular.module('app', ['ui.router.router']); |
| * |
| * app.config(function ($urlRouterProvider) { |
| * |
| * // Prevent $urlRouter from automatically intercepting URL changes; |
| * // this allows you to configure custom behavior in between |
| * // location changes and route synchronization: |
| * $urlRouterProvider.deferIntercept(); |
| * |
| * }).run(function ($rootScope, $urlRouter, UserService) { |
| * |
| * $rootScope.$on('$locationChangeSuccess', function(e) { |
| * // UserService is an example service for managing user state |
| * if (UserService.isLoggedIn()) return; |
| * |
| * // Prevent $urlRouter's default handler from firing |
| * e.preventDefault(); |
| * |
| * UserService.handleLogin().then(function() { |
| * // Once the user has logged in, sync the current URL |
| * // to the router: |
| * $urlRouter.sync(); |
| * }); |
| * }); |
| * |
| * // Configures $urlRouter's listener *after* your custom listener |
| * $urlRouter.listen(); |
| * }); |
| * </pre> |
| * |
| * @param {boolean} defer Indicates whether to defer location change interception. Passing |
| no parameter is equivalent to `true`. |
| */ |
| this.deferIntercept = function (defer) { |
| if (defer === undefined) defer = true; |
| interceptDeferred = defer; |
| }; |
| |
| /** |
| * @ngdoc object |
| * @name ui.router.router.$urlRouter |
| * |
| * @requires $location |
| * @requires $rootScope |
| * @requires $injector |
| * @requires $browser |
| * |
| * @description |
| * |
| */ |
| this.$get = $get; |
| $get.$inject = ['$location', '$rootScope', '$injector', '$browser']; |
| function $get( $location, $rootScope, $injector, $browser) { |
| |
| var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl; |
| |
| function appendBasePath(url, isHtml5, absolute) { |
| if (baseHref === '/') return url; |
| if (isHtml5) return baseHref.slice(0, -1) + url; |
| if (absolute) return baseHref.slice(1) + url; |
| return url; |
| } |
| |
| // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree |
| function update(evt) { |
| if (evt && evt.defaultPrevented) return; |
| var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl; |
| lastPushedUrl = undefined; |
| // TODO: Re-implement this in 1.0 for https://github.com/angular-ui/ui-router/issues/1573 |
| //if (ignoreUpdate) return true; |
| |
| function check(rule) { |
| var handled = rule($injector, $location); |
| |
| if (!handled) return false; |
| if (isString(handled)) $location.replace().url(handled); |
| return true; |
| } |
| var n = rules.length, i; |
| |
| for (i = 0; i < n; i++) { |
| if (check(rules[i])) return; |
| } |
| // always check otherwise last to allow dynamic updates to the set of rules |
| if (otherwise) check(otherwise); |
| } |
| |
| function listen() { |
| listener = listener || $rootScope.$on('$locationChangeSuccess', update); |
| return listener; |
| } |
| |
| if (!interceptDeferred) listen(); |
| |
| 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 |
| * <pre> |
| * 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(); |
| * }); |
| * }); |
| * </pre> |
| */ |
| sync: function() { |
| update(); |
| }, |
| |
| listen: function() { |
| return listen(); |
| }, |
| |
| update: function(read) { |
| if (read) { |
| location = $location.url(); |
| return; |
| } |
| if ($location.url() === location) return; |
| |
| $location.url(location); |
| $location.replace(); |
| }, |
| |
| push: function(urlMatcher, params, options) { |
| var url = urlMatcher.format(params || {}); |
| |
| // Handle the special hash param, if needed |
| if (url !== null && params && params['#']) { |
| url += '#' + params['#']; |
| } |
| |
| $location.url(url); |
| lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined; |
| if (options && options.replace) $location.replace(); |
| }, |
| |
| /** |
| * @ngdoc function |
| * @name ui.router.router.$urlRouter#href |
| * @methodOf ui.router.router.$urlRouter |
| * |
| * @description |
| * A URL generation method that returns the compiled URL for a given |
| * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters. |
| * |
| * @example |
| * <pre> |
| * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), { |
| * person: "bob" |
| * }); |
| * // $bob == "/about/bob"; |
| * </pre> |
| * |
| * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate. |
| * @param {object=} params An object of parameter values to fill the matcher's required parameters. |
| * @param {object=} options Options object. The options are: |
| * |
| * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl". |
| * |
| * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher` |
| */ |
| href: function(urlMatcher, params, options) { |
| if (!urlMatcher.validates(params)) return null; |
| |
| var isHtml5 = $locationProvider.html5Mode(); |
| if (angular.isObject(isHtml5)) { |
| isHtml5 = isHtml5.enabled; |
| } |
| |
| var url = urlMatcher.format(params); |
| options = options || {}; |
| |
| if (!isHtml5 && url !== null) { |
| url = "#" + $locationProvider.hashPrefix() + url; |
| } |
| |
| // Handle special hash param, if needed |
| if (url !== null && params && params['#']) { |
| url += '#' + params['#']; |
| } |
| |
| url = appendBasePath(url, isHtml5, options.absolute); |
| |
| if (!options.absolute || !url) { |
| return url; |
| } |
| |
| var slash = (!isHtml5 && url ? '/' : ''), port = $location.port(); |
| port = (port === 80 || port === 443 ? '' : ':' + port); |
| |
| return [$location.protocol(), '://', $location.host(), port, slash, url].join(''); |
| } |
| }; |
| } |
| } |
| |
| angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider); |