arnaucube
arnaucube/commonroutesAppPublic Self-hosted repository
commonroutesApp/www/lib/angular-ui-router/src/urlRouter.js
www/lib/angular-ui-router/src/urlRouter.jsRaw
  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'];
 18function $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
413angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);