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