arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
socketioMEANseed/www/bower_components/angular-route/angular-route.js
www/bower_components/angular-route/angular-route.jsRaw
   1/**
   2 * @license AngularJS v1.6.1
   3 * (c) 2010-2016 Google, Inc. http://angularjs.org
   4 * License: MIT
   5 */
   6(function(window, angular) {'use strict';
   7
   8/* global shallowCopy: true */
   9
  10/**
  11 * Creates a shallow copy of an object, an array or a primitive.
  12 *
  13 * Assumes that there are no proto properties for objects.
  14 */
  15function shallowCopy(src, dst) {
  16  if (isArray(src)) {
  17    dst = dst || [];
  18
  19    for (var i = 0, ii = src.length; i < ii; i++) {
  20      dst[i] = src[i];
  21    }
  22  } else if (isObject(src)) {
  23    dst = dst || {};
  24
  25    for (var key in src) {
  26      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
  27        dst[key] = src[key];
  28      }
  29    }
  30  }
  31
  32  return dst || src;
  33}
  34
  35/* global shallowCopy: false */
  36
  37// `isArray` and `isObject` are necessary for `shallowCopy()` (included via `src/shallowCopy.js`).
  38// They are initialized inside the `$RouteProvider`, to ensure `window.angular` is available.
  39var isArray;
  40var isObject;
  41var isDefined;
  42
  43/**
  44 * @ngdoc module
  45 * @name ngRoute
  46 * @description
  47 *
  48 * # ngRoute
  49 *
  50 * The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
  51 *
  52 * ## Example
  53 * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
  54 *
  55 *
  56 * <div doc-module-components="ngRoute"></div>
  57 */
  58/* global -ngRouteModule */
  59var ngRouteModule = angular.
  60  module('ngRoute', []).
  61  provider('$route', $RouteProvider).
  62  // Ensure `$route` will be instantiated in time to capture the initial `$locationChangeSuccess`
  63  // event (unless explicitly disabled). This is necessary in case `ngView` is included in an
  64  // asynchronously loaded template.
  65  run(instantiateRoute);
  66var $routeMinErr = angular.$$minErr('ngRoute');
  67var isEagerInstantiationEnabled;
  68
  69
  70/**
  71 * @ngdoc provider
  72 * @name $routeProvider
  73 * @this
  74 *
  75 * @description
  76 *
  77 * Used for configuring routes.
  78 *
  79 * ## Example
  80 * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
  81 *
  82 * ## Dependencies
  83 * Requires the {@link ngRoute `ngRoute`} module to be installed.
  84 */
  85function $RouteProvider() {
  86  isArray = angular.isArray;
  87  isObject = angular.isObject;
  88  isDefined = angular.isDefined;
  89
  90  function inherit(parent, extra) {
  91    return angular.extend(Object.create(parent), extra);
  92  }
  93
  94  var routes = {};
  95
  96  /**
  97   * @ngdoc method
  98   * @name $routeProvider#when
  99   *
 100   * @param {string} path Route path (matched against `$location.path`). If `$location.path`
 101   *    contains redundant trailing slash or is missing one, the route will still match and the
 102   *    `$location.path` will be updated to add or drop the trailing slash to exactly match the
 103   *    route definition.
 104   *
 105   *    * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
 106   *        to the next slash are matched and stored in `$routeParams` under the given `name`
 107   *        when the route matches.
 108   *    * `path` can contain named groups starting with a colon and ending with a star:
 109   *        e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
 110   *        when the route matches.
 111   *    * `path` can contain optional named groups with a question mark: e.g.`:name?`.
 112   *
 113   *    For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
 114   *    `/color/brown/largecode/code/with/slashes/edit` and extract:
 115   *
 116   *    * `color: brown`
 117   *    * `largecode: code/with/slashes`.
 118   *
 119   *
 120   * @param {Object} route Mapping information to be assigned to `$route.current` on route
 121   *    match.
 122   *
 123   *    Object properties:
 124   *
 125   *    - `controller` – `{(string|Function)=}` – Controller fn that should be associated with
 126   *      newly created scope or the name of a {@link angular.Module#controller registered
 127   *      controller} if passed as a string.
 128   *    - `controllerAs` – `{string=}` – An identifier name for a reference to the controller.
 129   *      If present, the controller will be published to scope under the `controllerAs` name.
 130   *    - `template` – `{(string|Function)=}` – html template as a string or a function that
 131   *      returns an html template as a string which should be used by {@link
 132   *      ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
 133   *      This property takes precedence over `templateUrl`.
 134   *
 135   *      If `template` is a function, it will be called with the following parameters:
 136   *
 137   *      - `{Array.<Object>}` - route parameters extracted from the current
 138   *        `$location.path()` by applying the current route
 139   *
 140   *      One of `template` or `templateUrl` is required.
 141   *
 142   *    - `templateUrl` – `{(string|Function)=}` – path or function that returns a path to an html
 143   *      template that should be used by {@link ngRoute.directive:ngView ngView}.
 144   *
 145   *      If `templateUrl` is a function, it will be called with the following parameters:
 146   *
 147   *      - `{Array.<Object>}` - route parameters extracted from the current
 148   *        `$location.path()` by applying the current route
 149   *
 150   *      One of `templateUrl` or `template` is required.
 151   *
 152   *    - `resolve` - `{Object.<string, Function>=}` - An optional map of dependencies which should
 153   *      be injected into the controller. If any of these dependencies are promises, the router
 154   *      will wait for them all to be resolved or one to be rejected before the controller is
 155   *      instantiated.
 156   *      If all the promises are resolved successfully, the values of the resolved promises are
 157   *      injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
 158   *      fired. If any of the promises are rejected the
 159   *      {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired.
 160   *      For easier access to the resolved dependencies from the template, the `resolve` map will
 161   *      be available on the scope of the route, under `$resolve` (by default) or a custom name
 162   *      specified by the `resolveAs` property (see below). This can be particularly useful, when
 163   *      working with {@link angular.Module#component components} as route templates.<br />
 164   *      <div class="alert alert-warning">
 165   *        **Note:** If your scope already contains a property with this name, it will be hidden
 166   *        or overwritten. Make sure, you specify an appropriate name for this property, that
 167   *        does not collide with other properties on the scope.
 168   *      </div>
 169   *      The map object is:
 170   *
 171   *      - `key` – `{string}`: a name of a dependency to be injected into the controller.
 172   *      - `factory` - `{string|Function}`: If `string` then it is an alias for a service.
 173   *        Otherwise if function, then it is {@link auto.$injector#invoke injected}
 174   *        and the return value is treated as the dependency. If the result is a promise, it is
 175   *        resolved before its value is injected into the controller. Be aware that
 176   *        `ngRoute.$routeParams` will still refer to the previous route within these resolve
 177   *        functions.  Use `$route.current.params` to access the new route parameters, instead.
 178   *
 179   *    - `resolveAs` - `{string=}` - The name under which the `resolve` map will be available on
 180   *      the scope of the route. If omitted, defaults to `$resolve`.
 181   *
 182   *    - `redirectTo` – `{(string|Function)=}` – value to update
 183   *      {@link ng.$location $location} path with and trigger route redirection.
 184   *
 185   *      If `redirectTo` is a function, it will be called with the following parameters:
 186   *
 187   *      - `{Object.<string>}` - route parameters extracted from the current
 188   *        `$location.path()` by applying the current route templateUrl.
 189   *      - `{string}` - current `$location.path()`
 190   *      - `{Object}` - current `$location.search()`
 191   *
 192   *      The custom `redirectTo` function is expected to return a string which will be used
 193   *      to update `$location.url()`. If the function throws an error, no further processing will
 194   *      take place and the {@link ngRoute.$route#$routeChangeError $routeChangeError} event will
 195   *      be fired.
 196   *
 197   *      Routes that specify `redirectTo` will not have their controllers, template functions
 198   *      or resolves called, the `$location` will be changed to the redirect url and route
 199   *      processing will stop. The exception to this is if the `redirectTo` is a function that
 200   *      returns `undefined`. In this case the route transition occurs as though there was no
 201   *      redirection.
 202   *
 203   *    - `resolveRedirectTo` – `{Function=}` – a function that will (eventually) return the value
 204   *      to update {@link ng.$location $location} URL with and trigger route redirection. In
 205   *      contrast to `redirectTo`, dependencies can be injected into `resolveRedirectTo` and the
 206   *      return value can be either a string or a promise that will be resolved to a string.
 207   *
 208   *      Similar to `redirectTo`, if the return value is `undefined` (or a promise that gets
 209   *      resolved to `undefined`), no redirection takes place and the route transition occurs as
 210   *      though there was no redirection.
 211   *
 212   *      If the function throws an error or the returned promise gets rejected, no further
 213   *      processing will take place and the
 214   *      {@link ngRoute.$route#$routeChangeError $routeChangeError} event will be fired.
 215   *
 216   *      `redirectTo` takes precedence over `resolveRedirectTo`, so specifying both on the same
 217   *      route definition, will cause the latter to be ignored.
 218   *
 219   *    - `[reloadOnSearch=true]` - `{boolean=}` - reload route when only `$location.search()`
 220   *      or `$location.hash()` changes.
 221   *
 222   *      If the option is set to `false` and url in the browser changes, then
 223   *      `$routeUpdate` event is broadcasted on the root scope.
 224   *
 225   *    - `[caseInsensitiveMatch=false]` - `{boolean=}` - match routes without being case sensitive
 226   *
 227   *      If the option is set to `true`, then the particular route can be matched without being
 228   *      case sensitive
 229   *
 230   * @returns {Object} self
 231   *
 232   * @description
 233   * Adds a new route definition to the `$route` service.
 234   */
 235  this.when = function(path, route) {
 236    //copy original route object to preserve params inherited from proto chain
 237    var routeCopy = shallowCopy(route);
 238    if (angular.isUndefined(routeCopy.reloadOnSearch)) {
 239      routeCopy.reloadOnSearch = true;
 240    }
 241    if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
 242      routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
 243    }
 244    routes[path] = angular.extend(
 245      routeCopy,
 246      path && pathRegExp(path, routeCopy)
 247    );
 248
 249    // create redirection for trailing slashes
 250    if (path) {
 251      var redirectPath = (path[path.length - 1] === '/')
 252            ? path.substr(0, path.length - 1)
 253            : path + '/';
 254
 255      routes[redirectPath] = angular.extend(
 256        {redirectTo: path},
 257        pathRegExp(redirectPath, routeCopy)
 258      );
 259    }
 260
 261    return this;
 262  };
 263
 264  /**
 265   * @ngdoc property
 266   * @name $routeProvider#caseInsensitiveMatch
 267   * @description
 268   *
 269   * A boolean property indicating if routes defined
 270   * using this provider should be matched using a case insensitive
 271   * algorithm. Defaults to `false`.
 272   */
 273  this.caseInsensitiveMatch = false;
 274
 275   /**
 276    * @param path {string} path
 277    * @param opts {Object} options
 278    * @return {?Object}
 279    *
 280    * @description
 281    * Normalizes the given path, returning a regular expression
 282    * and the original path.
 283    *
 284    * Inspired by pathRexp in visionmedia/express/lib/utils.js.
 285    */
 286  function pathRegExp(path, opts) {
 287    var insensitive = opts.caseInsensitiveMatch,
 288        ret = {
 289          originalPath: path,
 290          regexp: path
 291        },
 292        keys = ret.keys = [];
 293
 294    path = path
 295      .replace(/([().])/g, '\\$1')
 296      .replace(/(\/)?:(\w+)(\*\?|[?*])?/g, function(_, slash, key, option) {
 297        var optional = (option === '?' || option === '*?') ? '?' : null;
 298        var star = (option === '*' || option === '*?') ? '*' : null;
 299        keys.push({ name: key, optional: !!optional });
 300        slash = slash || '';
 301        return ''
 302          + (optional ? '' : slash)
 303          + '(?:'
 304          + (optional ? slash : '')
 305          + (star && '(.+?)' || '([^/]+)')
 306          + (optional || '')
 307          + ')'
 308          + (optional || '');
 309      })
 310      .replace(/([/$*])/g, '\\$1');
 311
 312    ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
 313    return ret;
 314  }
 315
 316  /**
 317   * @ngdoc method
 318   * @name $routeProvider#otherwise
 319   *
 320   * @description
 321   * Sets route definition that will be used on route change when no other route definition
 322   * is matched.
 323   *
 324   * @param {Object|string} params Mapping information to be assigned to `$route.current`.
 325   * If called with a string, the value maps to `redirectTo`.
 326   * @returns {Object} self
 327   */
 328  this.otherwise = function(params) {
 329    if (typeof params === 'string') {
 330      params = {redirectTo: params};
 331    }
 332    this.when(null, params);
 333    return this;
 334  };
 335
 336  /**
 337   * @ngdoc method
 338   * @name $routeProvider#eagerInstantiationEnabled
 339   * @kind function
 340   *
 341   * @description
 342   * Call this method as a setter to enable/disable eager instantiation of the
 343   * {@link ngRoute.$route $route} service upon application bootstrap. You can also call it as a
 344   * getter (i.e. without any arguments) to get the current value of the
 345   * `eagerInstantiationEnabled` flag.
 346   *
 347   * Instantiating `$route` early is necessary for capturing the initial
 348   * {@link ng.$location#$locationChangeStart $locationChangeStart} event and navigating to the
 349   * appropriate route. Usually, `$route` is instantiated in time by the
 350   * {@link ngRoute.ngView ngView} directive. Yet, in cases where `ngView` is included in an
 351   * asynchronously loaded template (e.g. in another directive's template), the directive factory
 352   * might not be called soon enough for `$route` to be instantiated _before_ the initial
 353   * `$locationChangeSuccess` event is fired. Eager instantiation ensures that `$route` is always
 354   * instantiated in time, regardless of when `ngView` will be loaded.
 355   *
 356   * The default value is true.
 357   *
 358   * **Note**:<br />
 359   * You may want to disable the default behavior when unit-testing modules that depend on
 360   * `ngRoute`, in order to avoid an unexpected request for the default route's template.
 361   *
 362   * @param {boolean=} enabled - If provided, update the internal `eagerInstantiationEnabled` flag.
 363   *
 364   * @returns {*} The current value of the `eagerInstantiationEnabled` flag if used as a getter or
 365   *     itself (for chaining) if used as a setter.
 366   */
 367  isEagerInstantiationEnabled = true;
 368  this.eagerInstantiationEnabled = function eagerInstantiationEnabled(enabled) {
 369    if (isDefined(enabled)) {
 370      isEagerInstantiationEnabled = enabled;
 371      return this;
 372    }
 373
 374    return isEagerInstantiationEnabled;
 375  };
 376
 377
 378  this.$get = ['$rootScope',
 379               '$location',
 380               '$routeParams',
 381               '$q',
 382               '$injector',
 383               '$templateRequest',
 384               '$sce',
 385      function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
 386
 387    /**
 388     * @ngdoc service
 389     * @name $route
 390     * @requires $location
 391     * @requires $routeParams
 392     *
 393     * @property {Object} current Reference to the current route definition.
 394     * The route definition contains:
 395     *
 396     *   - `controller`: The controller constructor as defined in the route definition.
 397     *   - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
 398     *     controller instantiation. The `locals` contain
 399     *     the resolved values of the `resolve` map. Additionally the `locals` also contain:
 400     *
 401     *     - `$scope` - The current route scope.
 402     *     - `$template` - The current route template HTML.
 403     *
 404     *     The `locals` will be assigned to the route scope's `$resolve` property. You can override
 405     *     the property name, using `resolveAs` in the route definition. See
 406     *     {@link ngRoute.$routeProvider $routeProvider} for more info.
 407     *
 408     * @property {Object} routes Object with all route configuration Objects as its properties.
 409     *
 410     * @description
 411     * `$route` is used for deep-linking URLs to controllers and views (HTML partials).
 412     * It watches `$location.url()` and tries to map the path to an existing route definition.
 413     *
 414     * Requires the {@link ngRoute `ngRoute`} module to be installed.
 415     *
 416     * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
 417     *
 418     * The `$route` service is typically used in conjunction with the
 419     * {@link ngRoute.directive:ngView `ngView`} directive and the
 420     * {@link ngRoute.$routeParams `$routeParams`} service.
 421     *
 422     * @example
 423     * This example shows how changing the URL hash causes the `$route` to match a route against the
 424     * URL, and the `ngView` pulls in the partial.
 425     *
 426     * <example name="$route-service" module="ngRouteExample"
 427     *          deps="angular-route.js" fixBase="true">
 428     *   <file name="index.html">
 429     *     <div ng-controller="MainController">
 430     *       Choose:
 431     *       <a href="Book/Moby">Moby</a> |
 432     *       <a href="Book/Moby/ch/1">Moby: Ch1</a> |
 433     *       <a href="Book/Gatsby">Gatsby</a> |
 434     *       <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
 435     *       <a href="Book/Scarlet">Scarlet Letter</a><br/>
 436     *
 437     *       <div ng-view></div>
 438     *
 439     *       <hr />
 440     *
 441     *       <pre>$location.path() = {{$location.path()}}</pre>
 442     *       <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
 443     *       <pre>$route.current.params = {{$route.current.params}}</pre>
 444     *       <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
 445     *       <pre>$routeParams = {{$routeParams}}</pre>
 446     *     </div>
 447     *   </file>
 448     *
 449     *   <file name="book.html">
 450     *     controller: {{name}}<br />
 451     *     Book Id: {{params.bookId}}<br />
 452     *   </file>
 453     *
 454     *   <file name="chapter.html">
 455     *     controller: {{name}}<br />
 456     *     Book Id: {{params.bookId}}<br />
 457     *     Chapter Id: {{params.chapterId}}
 458     *   </file>
 459     *
 460     *   <file name="script.js">
 461     *     angular.module('ngRouteExample', ['ngRoute'])
 462     *
 463     *      .controller('MainController', function($scope, $route, $routeParams, $location) {
 464     *          $scope.$route = $route;
 465     *          $scope.$location = $location;
 466     *          $scope.$routeParams = $routeParams;
 467     *      })
 468     *
 469     *      .controller('BookController', function($scope, $routeParams) {
 470     *          $scope.name = 'BookController';
 471     *          $scope.params = $routeParams;
 472     *      })
 473     *
 474     *      .controller('ChapterController', function($scope, $routeParams) {
 475     *          $scope.name = 'ChapterController';
 476     *          $scope.params = $routeParams;
 477     *      })
 478     *
 479     *     .config(function($routeProvider, $locationProvider) {
 480     *       $routeProvider
 481     *        .when('/Book/:bookId', {
 482     *         templateUrl: 'book.html',
 483     *         controller: 'BookController',
 484     *         resolve: {
 485     *           // I will cause a 1 second delay
 486     *           delay: function($q, $timeout) {
 487     *             var delay = $q.defer();
 488     *             $timeout(delay.resolve, 1000);
 489     *             return delay.promise;
 490     *           }
 491     *         }
 492     *       })
 493     *       .when('/Book/:bookId/ch/:chapterId', {
 494     *         templateUrl: 'chapter.html',
 495     *         controller: 'ChapterController'
 496     *       });
 497     *
 498     *       // configure html5 to get links working on jsfiddle
 499     *       $locationProvider.html5Mode(true);
 500     *     });
 501     *
 502     *   </file>
 503     *
 504     *   <file name="protractor.js" type="protractor">
 505     *     it('should load and compile correct template', function() {
 506     *       element(by.linkText('Moby: Ch1')).click();
 507     *       var content = element(by.css('[ng-view]')).getText();
 508     *       expect(content).toMatch(/controller: ChapterController/);
 509     *       expect(content).toMatch(/Book Id: Moby/);
 510     *       expect(content).toMatch(/Chapter Id: 1/);
 511     *
 512     *       element(by.partialLinkText('Scarlet')).click();
 513     *
 514     *       content = element(by.css('[ng-view]')).getText();
 515     *       expect(content).toMatch(/controller: BookController/);
 516     *       expect(content).toMatch(/Book Id: Scarlet/);
 517     *     });
 518     *   </file>
 519     * </example>
 520     */
 521
 522    /**
 523     * @ngdoc event
 524     * @name $route#$routeChangeStart
 525     * @eventType broadcast on root scope
 526     * @description
 527     * Broadcasted before a route change. At this  point the route services starts
 528     * resolving all of the dependencies needed for the route change to occur.
 529     * Typically this involves fetching the view template as well as any dependencies
 530     * defined in `resolve` route property. Once  all of the dependencies are resolved
 531     * `$routeChangeSuccess` is fired.
 532     *
 533     * The route change (and the `$location` change that triggered it) can be prevented
 534     * by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
 535     * for more details about event object.
 536     *
 537     * @param {Object} angularEvent Synthetic event object.
 538     * @param {Route} next Future route information.
 539     * @param {Route} current Current route information.
 540     */
 541
 542    /**
 543     * @ngdoc event
 544     * @name $route#$routeChangeSuccess
 545     * @eventType broadcast on root scope
 546     * @description
 547     * Broadcasted after a route change has happened successfully.
 548     * The `resolve` dependencies are now available in the `current.locals` property.
 549     *
 550     * {@link ngRoute.directive:ngView ngView} listens for the directive
 551     * to instantiate the controller and render the view.
 552     *
 553     * @param {Object} angularEvent Synthetic event object.
 554     * @param {Route} current Current route information.
 555     * @param {Route|Undefined} previous Previous route information, or undefined if current is
 556     * first route entered.
 557     */
 558
 559    /**
 560     * @ngdoc event
 561     * @name $route#$routeChangeError
 562     * @eventType broadcast on root scope
 563     * @description
 564     * Broadcasted if a redirection function fails or any redirection or resolve promises are
 565     * rejected.
 566     *
 567     * @param {Object} angularEvent Synthetic event object
 568     * @param {Route} current Current route information.
 569     * @param {Route} previous Previous route information.
 570     * @param {Route} rejection The thrown error or the rejection reason of the promise. Usually
 571     * the rejection reason is the error that caused the promise to get rejected.
 572     */
 573
 574    /**
 575     * @ngdoc event
 576     * @name $route#$routeUpdate
 577     * @eventType broadcast on root scope
 578     * @description
 579     * The `reloadOnSearch` property has been set to false, and we are reusing the same
 580     * instance of the Controller.
 581     *
 582     * @param {Object} angularEvent Synthetic event object
 583     * @param {Route} current Current/previous route information.
 584     */
 585
 586    var forceReload = false,
 587        preparedRoute,
 588        preparedRouteIsUpdateOnly,
 589        $route = {
 590          routes: routes,
 591
 592          /**
 593           * @ngdoc method
 594           * @name $route#reload
 595           *
 596           * @description
 597           * Causes `$route` service to reload the current route even if
 598           * {@link ng.$location $location} hasn't changed.
 599           *
 600           * As a result of that, {@link ngRoute.directive:ngView ngView}
 601           * creates new scope and reinstantiates the controller.
 602           */
 603          reload: function() {
 604            forceReload = true;
 605
 606            var fakeLocationEvent = {
 607              defaultPrevented: false,
 608              preventDefault: function fakePreventDefault() {
 609                this.defaultPrevented = true;
 610                forceReload = false;
 611              }
 612            };
 613
 614            $rootScope.$evalAsync(function() {
 615              prepareRoute(fakeLocationEvent);
 616              if (!fakeLocationEvent.defaultPrevented) commitRoute();
 617            });
 618          },
 619
 620          /**
 621           * @ngdoc method
 622           * @name $route#updateParams
 623           *
 624           * @description
 625           * Causes `$route` service to update the current URL, replacing
 626           * current route parameters with those specified in `newParams`.
 627           * Provided property names that match the route's path segment
 628           * definitions will be interpolated into the location's path, while
 629           * remaining properties will be treated as query params.
 630           *
 631           * @param {!Object<string, string>} newParams mapping of URL parameter names to values
 632           */
 633          updateParams: function(newParams) {
 634            if (this.current && this.current.$$route) {
 635              newParams = angular.extend({}, this.current.params, newParams);
 636              $location.path(interpolate(this.current.$$route.originalPath, newParams));
 637              // interpolate modifies newParams, only query params are left
 638              $location.search(newParams);
 639            } else {
 640              throw $routeMinErr('norout', 'Tried updating route when with no current route');
 641            }
 642          }
 643        };
 644
 645    $rootScope.$on('$locationChangeStart', prepareRoute);
 646    $rootScope.$on('$locationChangeSuccess', commitRoute);
 647
 648    return $route;
 649
 650    /////////////////////////////////////////////////////
 651
 652    /**
 653     * @param on {string} current url
 654     * @param route {Object} route regexp to match the url against
 655     * @return {?Object}
 656     *
 657     * @description
 658     * Check if the route matches the current url.
 659     *
 660     * Inspired by match in
 661     * visionmedia/express/lib/router/router.js.
 662     */
 663    function switchRouteMatcher(on, route) {
 664      var keys = route.keys,
 665          params = {};
 666
 667      if (!route.regexp) return null;
 668
 669      var m = route.regexp.exec(on);
 670      if (!m) return null;
 671
 672      for (var i = 1, len = m.length; i < len; ++i) {
 673        var key = keys[i - 1];
 674
 675        var val = m[i];
 676
 677        if (key && val) {
 678          params[key.name] = val;
 679        }
 680      }
 681      return params;
 682    }
 683
 684    function prepareRoute($locationEvent) {
 685      var lastRoute = $route.current;
 686
 687      preparedRoute = parseRoute();
 688      preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
 689          && angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
 690          && !preparedRoute.reloadOnSearch && !forceReload;
 691
 692      if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
 693        if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
 694          if ($locationEvent) {
 695            $locationEvent.preventDefault();
 696          }
 697        }
 698      }
 699    }
 700
 701    function commitRoute() {
 702      var lastRoute = $route.current;
 703      var nextRoute = preparedRoute;
 704
 705      if (preparedRouteIsUpdateOnly) {
 706        lastRoute.params = nextRoute.params;
 707        angular.copy(lastRoute.params, $routeParams);
 708        $rootScope.$broadcast('$routeUpdate', lastRoute);
 709      } else if (nextRoute || lastRoute) {
 710        forceReload = false;
 711        $route.current = nextRoute;
 712
 713        var nextRoutePromise = $q.resolve(nextRoute);
 714
 715        nextRoutePromise.
 716          then(getRedirectionData).
 717          then(handlePossibleRedirection).
 718          then(function(keepProcessingRoute) {
 719            return keepProcessingRoute && nextRoutePromise.
 720              then(resolveLocals).
 721              then(function(locals) {
 722                // after route change
 723                if (nextRoute === $route.current) {
 724                  if (nextRoute) {
 725                    nextRoute.locals = locals;
 726                    angular.copy(nextRoute.params, $routeParams);
 727                  }
 728                  $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
 729                }
 730              });
 731          }).catch(function(error) {
 732            if (nextRoute === $route.current) {
 733              $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
 734            }
 735          });
 736      }
 737    }
 738
 739    function getRedirectionData(route) {
 740      var data = {
 741        route: route,
 742        hasRedirection: false
 743      };
 744
 745      if (route) {
 746        if (route.redirectTo) {
 747          if (angular.isString(route.redirectTo)) {
 748            data.path = interpolate(route.redirectTo, route.params);
 749            data.search = route.params;
 750            data.hasRedirection = true;
 751          } else {
 752            var oldPath = $location.path();
 753            var oldSearch = $location.search();
 754            var newUrl = route.redirectTo(route.pathParams, oldPath, oldSearch);
 755
 756            if (angular.isDefined(newUrl)) {
 757              data.url = newUrl;
 758              data.hasRedirection = true;
 759            }
 760          }
 761        } else if (route.resolveRedirectTo) {
 762          return $q.
 763            resolve($injector.invoke(route.resolveRedirectTo)).
 764            then(function(newUrl) {
 765              if (angular.isDefined(newUrl)) {
 766                data.url = newUrl;
 767                data.hasRedirection = true;
 768              }
 769
 770              return data;
 771            });
 772        }
 773      }
 774
 775      return data;
 776    }
 777
 778    function handlePossibleRedirection(data) {
 779      var keepProcessingRoute = true;
 780
 781      if (data.route !== $route.current) {
 782        keepProcessingRoute = false;
 783      } else if (data.hasRedirection) {
 784        var oldUrl = $location.url();
 785        var newUrl = data.url;
 786
 787        if (newUrl) {
 788          $location.
 789            url(newUrl).
 790            replace();
 791        } else {
 792          newUrl = $location.
 793            path(data.path).
 794            search(data.search).
 795            replace().
 796            url();
 797        }
 798
 799        if (newUrl !== oldUrl) {
 800          // Exit out and don't process current next value,
 801          // wait for next location change from redirect
 802          keepProcessingRoute = false;
 803        }
 804      }
 805
 806      return keepProcessingRoute;
 807    }
 808
 809    function resolveLocals(route) {
 810      if (route) {
 811        var locals = angular.extend({}, route.resolve);
 812        angular.forEach(locals, function(value, key) {
 813          locals[key] = angular.isString(value) ?
 814              $injector.get(value) :
 815              $injector.invoke(value, null, null, key);
 816        });
 817        var template = getTemplateFor(route);
 818        if (angular.isDefined(template)) {
 819          locals['$template'] = template;
 820        }
 821        return $q.all(locals);
 822      }
 823    }
 824
 825    function getTemplateFor(route) {
 826      var template, templateUrl;
 827      if (angular.isDefined(template = route.template)) {
 828        if (angular.isFunction(template)) {
 829          template = template(route.params);
 830        }
 831      } else if (angular.isDefined(templateUrl = route.templateUrl)) {
 832        if (angular.isFunction(templateUrl)) {
 833          templateUrl = templateUrl(route.params);
 834        }
 835        if (angular.isDefined(templateUrl)) {
 836          route.loadedTemplateUrl = $sce.valueOf(templateUrl);
 837          template = $templateRequest(templateUrl);
 838        }
 839      }
 840      return template;
 841    }
 842
 843    /**
 844     * @returns {Object} the current active route, by matching it against the URL
 845     */
 846    function parseRoute() {
 847      // Match a route
 848      var params, match;
 849      angular.forEach(routes, function(route, path) {
 850        if (!match && (params = switchRouteMatcher($location.path(), route))) {
 851          match = inherit(route, {
 852            params: angular.extend({}, $location.search(), params),
 853            pathParams: params});
 854          match.$$route = route;
 855        }
 856      });
 857      // No route matched; fallback to "otherwise" route
 858      return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
 859    }
 860
 861    /**
 862     * @returns {string} interpolation of the redirect path with the parameters
 863     */
 864    function interpolate(string, params) {
 865      var result = [];
 866      angular.forEach((string || '').split(':'), function(segment, i) {
 867        if (i === 0) {
 868          result.push(segment);
 869        } else {
 870          var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
 871          var key = segmentMatch[1];
 872          result.push(params[key]);
 873          result.push(segmentMatch[2] || '');
 874          delete params[key];
 875        }
 876      });
 877      return result.join('');
 878    }
 879  }];
 880}
 881
 882instantiateRoute.$inject = ['$injector'];
 883function instantiateRoute($injector) {
 884  if (isEagerInstantiationEnabled) {
 885    // Instantiate `$route`
 886    $injector.get('$route');
 887  }
 888}
 889
 890ngRouteModule.provider('$routeParams', $RouteParamsProvider);
 891
 892
 893/**
 894 * @ngdoc service
 895 * @name $routeParams
 896 * @requires $route
 897 * @this
 898 *
 899 * @description
 900 * The `$routeParams` service allows you to retrieve the current set of route parameters.
 901 *
 902 * Requires the {@link ngRoute `ngRoute`} module to be installed.
 903 *
 904 * The route parameters are a combination of {@link ng.$location `$location`}'s
 905 * {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}.
 906 * The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
 907 *
 908 * In case of parameter name collision, `path` params take precedence over `search` params.
 909 *
 910 * The service guarantees that the identity of the `$routeParams` object will remain unchanged
 911 * (but its properties will likely change) even when a route change occurs.
 912 *
 913 * Note that the `$routeParams` are only updated *after* a route change completes successfully.
 914 * This means that you cannot rely on `$routeParams` being correct in route resolve functions.
 915 * Instead you can use `$route.current.params` to access the new route's parameters.
 916 *
 917 * @example
 918 * ```js
 919 *  // Given:
 920 *  // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
 921 *  // Route: /Chapter/:chapterId/Section/:sectionId
 922 *  //
 923 *  // Then
 924 *  $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
 925 * ```
 926 */
 927function $RouteParamsProvider() {
 928  this.$get = function() { return {}; };
 929}
 930
 931ngRouteModule.directive('ngView', ngViewFactory);
 932ngRouteModule.directive('ngView', ngViewFillContentFactory);
 933
 934
 935/**
 936 * @ngdoc directive
 937 * @name ngView
 938 * @restrict ECA
 939 *
 940 * @description
 941 * # Overview
 942 * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
 943 * including the rendered template of the current route into the main layout (`index.html`) file.
 944 * Every time the current route changes, the included view changes with it according to the
 945 * configuration of the `$route` service.
 946 *
 947 * Requires the {@link ngRoute `ngRoute`} module to be installed.
 948 *
 949 * @animations
 950 * | Animation                        | Occurs                              |
 951 * |----------------------------------|-------------------------------------|
 952 * | {@link ng.$animate#enter enter}  | when the new element is inserted to the DOM |
 953 * | {@link ng.$animate#leave leave}  | when the old element is removed from to the DOM  |
 954 *
 955 * The enter and leave animation occur concurrently.
 956 *
 957 * @scope
 958 * @priority 400
 959 * @param {string=} onload Expression to evaluate whenever the view updates.
 960 *
 961 * @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
 962 *                  $anchorScroll} to scroll the viewport after the view is updated.
 963 *
 964 *                  - If the attribute is not set, disable scrolling.
 965 *                  - If the attribute is set without value, enable scrolling.
 966 *                  - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
 967 *                    as an expression yields a truthy value.
 968 * @example
 969    <example name="ngView-directive" module="ngViewExample"
 970             deps="angular-route.js;angular-animate.js"
 971             animations="true" fixBase="true">
 972      <file name="index.html">
 973        <div ng-controller="MainCtrl as main">
 974          Choose:
 975          <a href="Book/Moby">Moby</a> |
 976          <a href="Book/Moby/ch/1">Moby: Ch1</a> |
 977          <a href="Book/Gatsby">Gatsby</a> |
 978          <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
 979          <a href="Book/Scarlet">Scarlet Letter</a><br/>
 980
 981          <div class="view-animate-container">
 982            <div ng-view class="view-animate"></div>
 983          </div>
 984          <hr />
 985
 986          <pre>$location.path() = {{main.$location.path()}}</pre>
 987          <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
 988          <pre>$route.current.params = {{main.$route.current.params}}</pre>
 989          <pre>$routeParams = {{main.$routeParams}}</pre>
 990        </div>
 991      </file>
 992
 993      <file name="book.html">
 994        <div>
 995          controller: {{book.name}}<br />
 996          Book Id: {{book.params.bookId}}<br />
 997        </div>
 998      </file>
 999
1000      <file name="chapter.html">
1001        <div>
1002          controller: {{chapter.name}}<br />
1003          Book Id: {{chapter.params.bookId}}<br />
1004          Chapter Id: {{chapter.params.chapterId}}
1005        </div>
1006      </file>
1007
1008      <file name="animations.css">
1009        .view-animate-container {
1010          position:relative;
1011          height:100px!important;
1012          background:white;
1013          border:1px solid black;
1014          height:40px;
1015          overflow:hidden;
1016        }
1017
1018        .view-animate {
1019          padding:10px;
1020        }
1021
1022        .view-animate.ng-enter, .view-animate.ng-leave {
1023          transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
1024
1025          display:block;
1026          width:100%;
1027          border-left:1px solid black;
1028
1029          position:absolute;
1030          top:0;
1031          left:0;
1032          right:0;
1033          bottom:0;
1034          padding:10px;
1035        }
1036
1037        .view-animate.ng-enter {
1038          left:100%;
1039        }
1040        .view-animate.ng-enter.ng-enter-active {
1041          left:0;
1042        }
1043        .view-animate.ng-leave.ng-leave-active {
1044          left:-100%;
1045        }
1046      </file>
1047
1048      <file name="script.js">
1049        angular.module('ngViewExample', ['ngRoute', 'ngAnimate'])
1050          .config(['$routeProvider', '$locationProvider',
1051            function($routeProvider, $locationProvider) {
1052              $routeProvider
1053                .when('/Book/:bookId', {
1054                  templateUrl: 'book.html',
1055                  controller: 'BookCtrl',
1056                  controllerAs: 'book'
1057                })
1058                .when('/Book/:bookId/ch/:chapterId', {
1059                  templateUrl: 'chapter.html',
1060                  controller: 'ChapterCtrl',
1061                  controllerAs: 'chapter'
1062                });
1063
1064              $locationProvider.html5Mode(true);
1065          }])
1066          .controller('MainCtrl', ['$route', '$routeParams', '$location',
1067            function MainCtrl($route, $routeParams, $location) {
1068              this.$route = $route;
1069              this.$location = $location;
1070              this.$routeParams = $routeParams;
1071          }])
1072          .controller('BookCtrl', ['$routeParams', function BookCtrl($routeParams) {
1073            this.name = 'BookCtrl';
1074            this.params = $routeParams;
1075          }])
1076          .controller('ChapterCtrl', ['$routeParams', function ChapterCtrl($routeParams) {
1077            this.name = 'ChapterCtrl';
1078            this.params = $routeParams;
1079          }]);
1080
1081      </file>
1082
1083      <file name="protractor.js" type="protractor">
1084        it('should load and compile correct template', function() {
1085          element(by.linkText('Moby: Ch1')).click();
1086          var content = element(by.css('[ng-view]')).getText();
1087          expect(content).toMatch(/controller: ChapterCtrl/);
1088          expect(content).toMatch(/Book Id: Moby/);
1089          expect(content).toMatch(/Chapter Id: 1/);
1090
1091          element(by.partialLinkText('Scarlet')).click();
1092
1093          content = element(by.css('[ng-view]')).getText();
1094          expect(content).toMatch(/controller: BookCtrl/);
1095          expect(content).toMatch(/Book Id: Scarlet/);
1096        });
1097      </file>
1098    </example>
1099 */
1100
1101
1102/**
1103 * @ngdoc event
1104 * @name ngView#$viewContentLoaded
1105 * @eventType emit on the current ngView scope
1106 * @description
1107 * Emitted every time the ngView content is reloaded.
1108 */
1109ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
1110function ngViewFactory($route, $anchorScroll, $animate) {
1111  return {
1112    restrict: 'ECA',
1113    terminal: true,
1114    priority: 400,
1115    transclude: 'element',
1116    link: function(scope, $element, attr, ctrl, $transclude) {
1117        var currentScope,
1118            currentElement,
1119            previousLeaveAnimation,
1120            autoScrollExp = attr.autoscroll,
1121            onloadExp = attr.onload || '';
1122
1123        scope.$on('$routeChangeSuccess', update);
1124        update();
1125
1126        function cleanupLastView() {
1127          if (previousLeaveAnimation) {
1128            $animate.cancel(previousLeaveAnimation);
1129            previousLeaveAnimation = null;
1130          }
1131
1132          if (currentScope) {
1133            currentScope.$destroy();
1134            currentScope = null;
1135          }
1136          if (currentElement) {
1137            previousLeaveAnimation = $animate.leave(currentElement);
1138            previousLeaveAnimation.done(function(response) {
1139              if (response !== false) previousLeaveAnimation = null;
1140            });
1141            currentElement = null;
1142          }
1143        }
1144
1145        function update() {
1146          var locals = $route.current && $route.current.locals,
1147              template = locals && locals.$template;
1148
1149          if (angular.isDefined(template)) {
1150            var newScope = scope.$new();
1151            var current = $route.current;
1152
1153            // Note: This will also link all children of ng-view that were contained in the original
1154            // html. If that content contains controllers, ... they could pollute/change the scope.
1155            // However, using ng-view on an element with additional content does not make sense...
1156            // Note: We can't remove them in the cloneAttchFn of $transclude as that
1157            // function is called before linking the content, which would apply child
1158            // directives to non existing elements.
1159            var clone = $transclude(newScope, function(clone) {
1160              $animate.enter(clone, null, currentElement || $element).done(function onNgViewEnter(response) {
1161                if (response !== false && angular.isDefined(autoScrollExp)
1162                  && (!autoScrollExp || scope.$eval(autoScrollExp))) {
1163                  $anchorScroll();
1164                }
1165              });
1166              cleanupLastView();
1167            });
1168
1169            currentElement = clone;
1170            currentScope = current.scope = newScope;
1171            currentScope.$emit('$viewContentLoaded');
1172            currentScope.$eval(onloadExp);
1173          } else {
1174            cleanupLastView();
1175          }
1176        }
1177    }
1178  };
1179}
1180
1181// This directive is called during the $transclude call of the first `ngView` directive.
1182// It will replace and compile the content of the element with the loaded template.
1183// We need this directive so that the element content is already filled when
1184// the link function of another directive on the same element as ngView
1185// is called.
1186ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
1187function ngViewFillContentFactory($compile, $controller, $route) {
1188  return {
1189    restrict: 'ECA',
1190    priority: -400,
1191    link: function(scope, $element) {
1192      var current = $route.current,
1193          locals = current.locals;
1194
1195      $element.html(locals.$template);
1196
1197      var link = $compile($element.contents());
1198
1199      if (current.controller) {
1200        locals.$scope = scope;
1201        var controller = $controller(current.controller, locals);
1202        if (current.controllerAs) {
1203          scope[current.controllerAs] = controller;
1204        }
1205        $element.data('$ngControllerController', controller);
1206        $element.children().data('$ngControllerController', controller);
1207      }
1208      scope[current.resolveAs || '$resolve'] = locals;
1209
1210      link(scope);
1211    }
1212  };
1213}
1214
1215
1216})(window, window.angular);