Merge "Add qtip job to pod zte-virtual6"
[releng.git] / utils / test / testapi / 3rd_party / static / testapi-ui / assets / lib / angular-ui-router / src / urlRouter.js
1 /**
2  * @ngdoc object
3  * @name ui.router.router.$urlRouterProvider
4  *
5  * @requires ui.router.util.$urlMatcherFactoryProvider
6  * @requires $locationProvider
7  *
8  * @description
9  * `$urlRouterProvider` has the responsibility of watching `$location`. 
10  * When `$location` changes it runs through a list of rules one by one until a 
11  * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify 
12  * a url in a state configuration. All urls are compiled into a UrlMatcher object.
13  *
14  * There are several methods on `$urlRouterProvider` that make it useful to use directly
15  * in your module config.
16  */
17 $UrlRouterProvider.$inject = ['$locationProvider', '$urlMatcherFactoryProvider'];
18 function $UrlRouterProvider(   $locationProvider,   $urlMatcherFactory) {
19   var rules = [], otherwise = null, interceptDeferred = false, listener;
20
21   // Returns a string that is a prefix of all strings matching the RegExp
22   function regExpPrefix(re) {
23     var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
24     return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
25   }
26
27   // Interpolates matched values into a String.replace()-style pattern
28   function interpolate(pattern, match) {
29     return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
30       return match[what === '$' ? 0 : Number(what)];
31     });
32   }
33
34   /**
35    * @ngdoc function
36    * @name ui.router.router.$urlRouterProvider#rule
37    * @methodOf ui.router.router.$urlRouterProvider
38    *
39    * @description
40    * Defines rules that are used by `$urlRouterProvider` to find matches for
41    * specific URLs.
42    *
43    * @example
44    * <pre>
45    * var app = angular.module('app', ['ui.router.router']);
46    *
47    * app.config(function ($urlRouterProvider) {
48    *   // Here's an example of how you might allow case insensitive urls
49    *   $urlRouterProvider.rule(function ($injector, $location) {
50    *     var path = $location.path(),
51    *         normalized = path.toLowerCase();
52    *
53    *     if (path !== normalized) {
54    *       return normalized;
55    *     }
56    *   });
57    * });
58    * </pre>
59    *
60    * @param {object} rule Handler function that takes `$injector` and `$location`
61    * services as arguments. You can use them to return a valid path as a string.
62    *
63    * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
64    */
65   this.rule = function (rule) {
66     if (!isFunction(rule)) throw new Error("'rule' must be a function");
67     rules.push(rule);
68     return this;
69   };
70
71   /**
72    * @ngdoc object
73    * @name ui.router.router.$urlRouterProvider#otherwise
74    * @methodOf ui.router.router.$urlRouterProvider
75    *
76    * @description
77    * Defines a path that is used when an invalid route is requested.
78    *
79    * @example
80    * <pre>
81    * var app = angular.module('app', ['ui.router.router']);
82    *
83    * app.config(function ($urlRouterProvider) {
84    *   // if the path doesn't match any of the urls you configured
85    *   // otherwise will take care of routing the user to the
86    *   // specified url
87    *   $urlRouterProvider.otherwise('/index');
88    *
89    *   // Example of using function rule as param
90    *   $urlRouterProvider.otherwise(function ($injector, $location) {
91    *     return '/a/valid/url';
92    *   });
93    * });
94    * </pre>
95    *
96    * @param {string|object} rule The url path you want to redirect to or a function 
97    * rule that returns the url path. The function version is passed two params: 
98    * `$injector` and `$location` services, and must return a url string.
99    *
100    * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
101    */
102   this.otherwise = function (rule) {
103     if (isString(rule)) {
104       var redirect = rule;
105       rule = function () { return redirect; };
106     }
107     else if (!isFunction(rule)) throw new Error("'rule' must be a function");
108     otherwise = rule;
109     return this;
110   };
111
112
113   function handleIfMatch($injector, handler, match) {
114     if (!match) return false;
115     var result = $injector.invoke(handler, handler, { $match: match });
116     return isDefined(result) ? result : true;
117   }
118
119   /**
120    * @ngdoc function
121    * @name ui.router.router.$urlRouterProvider#when
122    * @methodOf ui.router.router.$urlRouterProvider
123    *
124    * @description
125    * Registers a handler for a given url matching. if handle is a string, it is
126    * treated as a redirect, and is interpolated according to the syntax of match
127    * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
128    *
129    * If the handler is a function, it is injectable. It gets invoked if `$location`
130    * matches. You have the option of inject the match object as `$match`.
131    *
132    * The handler can return
133    *
134    * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
135    *   will continue trying to find another one that matches.
136    * - **string** which is treated as a redirect and passed to `$location.url()`
137    * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
138    *
139    * @example
140    * <pre>
141    * var app = angular.module('app', ['ui.router.router']);
142    *
143    * app.config(function ($urlRouterProvider) {
144    *   $urlRouterProvider.when($state.url, function ($match, $stateParams) {
145    *     if ($state.$current.navigable !== state ||
146    *         !equalForKeys($match, $stateParams) {
147    *      $state.transitionTo(state, $match, false);
148    *     }
149    *   });
150    * });
151    * </pre>
152    *
153    * @param {string|object} what The incoming path that you want to redirect.
154    * @param {string|object} handler The path you want to redirect your user to.
155    */
156   this.when = function (what, handler) {
157     var redirect, handlerIsString = isString(handler);
158     if (isString(what)) what = $urlMatcherFactory.compile(what);
159
160     if (!handlerIsString && !isFunction(handler) && !isArray(handler))
161       throw new Error("invalid 'handler' in when()");
162
163     var strategies = {
164       matcher: function (what, handler) {
165         if (handlerIsString) {
166           redirect = $urlMatcherFactory.compile(handler);
167           handler = ['$match', function ($match) { return redirect.format($match); }];
168         }
169         return extend(function ($injector, $location) {
170           return handleIfMatch($injector, handler, what.exec($location.path(), $location.search()));
171         }, {
172           prefix: isString(what.prefix) ? what.prefix : ''
173         });
174       },
175       regex: function (what, handler) {
176         if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
177
178         if (handlerIsString) {
179           redirect = handler;
180           handler = ['$match', function ($match) { return interpolate(redirect, $match); }];
181         }
182         return extend(function ($injector, $location) {
183           return handleIfMatch($injector, handler, what.exec($location.path()));
184         }, {
185           prefix: regExpPrefix(what)
186         });
187       }
188     };
189
190     var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp };
191
192     for (var n in check) {
193       if (check[n]) return this.rule(strategies[n](what, handler));
194     }
195
196     throw new Error("invalid 'what' in when()");
197   };
198
199   /**
200    * @ngdoc function
201    * @name ui.router.router.$urlRouterProvider#deferIntercept
202    * @methodOf ui.router.router.$urlRouterProvider
203    *
204    * @description
205    * Disables (or enables) deferring location change interception.
206    *
207    * If you wish to customize the behavior of syncing the URL (for example, if you wish to
208    * defer a transition but maintain the current URL), call this method at configuration time.
209    * Then, at run time, call `$urlRouter.listen()` after you have configured your own
210    * `$locationChangeSuccess` event handler.
211    *
212    * @example
213    * <pre>
214    * var app = angular.module('app', ['ui.router.router']);
215    *
216    * app.config(function ($urlRouterProvider) {
217    *
218    *   // Prevent $urlRouter from automatically intercepting URL changes;
219    *   // this allows you to configure custom behavior in between
220    *   // location changes and route synchronization:
221    *   $urlRouterProvider.deferIntercept();
222    *
223    * }).run(function ($rootScope, $urlRouter, UserService) {
224    *
225    *   $rootScope.$on('$locationChangeSuccess', function(e) {
226    *     // UserService is an example service for managing user state
227    *     if (UserService.isLoggedIn()) return;
228    *
229    *     // Prevent $urlRouter's default handler from firing
230    *     e.preventDefault();
231    *
232    *     UserService.handleLogin().then(function() {
233    *       // Once the user has logged in, sync the current URL
234    *       // to the router:
235    *       $urlRouter.sync();
236    *     });
237    *   });
238    *
239    *   // Configures $urlRouter's listener *after* your custom listener
240    *   $urlRouter.listen();
241    * });
242    * </pre>
243    *
244    * @param {boolean} defer Indicates whether to defer location change interception. Passing
245             no parameter is equivalent to `true`.
246    */
247   this.deferIntercept = function (defer) {
248     if (defer === undefined) defer = true;
249     interceptDeferred = defer;
250   };
251
252   /**
253    * @ngdoc object
254    * @name ui.router.router.$urlRouter
255    *
256    * @requires $location
257    * @requires $rootScope
258    * @requires $injector
259    * @requires $browser
260    *
261    * @description
262    *
263    */
264   this.$get = $get;
265   $get.$inject = ['$location', '$rootScope', '$injector', '$browser'];
266   function $get(   $location,   $rootScope,   $injector,   $browser) {
267
268     var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl;
269
270     function appendBasePath(url, isHtml5, absolute) {
271       if (baseHref === '/') return url;
272       if (isHtml5) return baseHref.slice(0, -1) + url;
273       if (absolute) return baseHref.slice(1) + url;
274       return url;
275     }
276
277     // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
278     function update(evt) {
279       if (evt && evt.defaultPrevented) return;
280       var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl;
281       lastPushedUrl = undefined;
282       if (ignoreUpdate) return true;
283
284       function check(rule) {
285         var handled = rule($injector, $location);
286
287         if (!handled) return false;
288         if (isString(handled)) $location.replace().url(handled);
289         return true;
290       }
291       var n = rules.length, i;
292
293       for (i = 0; i < n; i++) {
294         if (check(rules[i])) return;
295       }
296       // always check otherwise last to allow dynamic updates to the set of rules
297       if (otherwise) check(otherwise);
298     }
299
300     function listen() {
301       listener = listener || $rootScope.$on('$locationChangeSuccess', update);
302       return listener;
303     }
304
305     if (!interceptDeferred) listen();
306
307     return {
308       /**
309        * @ngdoc function
310        * @name ui.router.router.$urlRouter#sync
311        * @methodOf ui.router.router.$urlRouter
312        *
313        * @description
314        * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`.
315        * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event,
316        * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed
317        * with the transition by calling `$urlRouter.sync()`.
318        *
319        * @example
320        * <pre>
321        * angular.module('app', ['ui.router'])
322        *   .run(function($rootScope, $urlRouter) {
323        *     $rootScope.$on('$locationChangeSuccess', function(evt) {
324        *       // Halt state change from even starting
325        *       evt.preventDefault();
326        *       // Perform custom logic
327        *       var meetsRequirement = ...
328        *       // Continue with the update and state transition if logic allows
329        *       if (meetsRequirement) $urlRouter.sync();
330        *     });
331        * });
332        * </pre>
333        */
334       sync: function() {
335         update();
336       },
337
338       listen: function() {
339         return listen();
340       },
341
342       update: function(read) {
343         if (read) {
344           location = $location.url();
345           return;
346         }
347         if ($location.url() === location) return;
348
349         $location.url(location);
350         $location.replace();
351       },
352
353       push: function(urlMatcher, params, options) {
354         $location.url(urlMatcher.format(params || {}));
355         lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined;
356         if (options && options.replace) $location.replace();
357       },
358
359       /**
360        * @ngdoc function
361        * @name ui.router.router.$urlRouter#href
362        * @methodOf ui.router.router.$urlRouter
363        *
364        * @description
365        * A URL generation method that returns the compiled URL for a given
366        * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters.
367        *
368        * @example
369        * <pre>
370        * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
371        *   person: "bob"
372        * });
373        * // $bob == "/about/bob";
374        * </pre>
375        *
376        * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate.
377        * @param {object=} params An object of parameter values to fill the matcher's required parameters.
378        * @param {object=} options Options object. The options are:
379        *
380        * - **`absolute`** - {boolean=false},  If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
381        *
382        * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher`
383        */
384       href: function(urlMatcher, params, options) {
385         if (!urlMatcher.validates(params)) return null;
386
387         var isHtml5 = $locationProvider.html5Mode();
388         if (angular.isObject(isHtml5)) {
389           isHtml5 = isHtml5.enabled;
390         }
391         
392         var url = urlMatcher.format(params);
393         options = options || {};
394
395         if (!isHtml5 && url !== null) {
396           url = "#" + $locationProvider.hashPrefix() + url;
397         }
398         url = appendBasePath(url, isHtml5, options.absolute);
399
400         if (!options.absolute || !url) {
401           return url;
402         }
403
404         var slash = (!isHtml5 && url ? '/' : ''), port = $location.port();
405         port = (port === 80 || port === 443 ? '' : ':' + port);
406
407         return [$location.protocol(), '://', $location.host(), port, slash, url].join('');
408       }
409     };
410   }
411 }
412
413 angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);