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