懒羊羊
2023-11-14 8286c62256f23bc2367a6729c0f46f84215e380b
提交 | 用户 | 时间
8286c6 1 /**
2  * @license AngularJS v1.2.13
3  * (c) 2010-2014 Google, Inc. http://angularjs.org
4  * License: MIT
5  */
6 (function(window, angular, undefined) {'use strict';
7
8 /**
9  * @ngdoc overview
10  * @name ngRoute
11  * @description
12  *
13  * # ngRoute
14  *
15  * The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
16  *
17  * ## Example
18  * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
19  * 
20  * {@installModule route}
21  *
22  * <div doc-module-components="ngRoute"></div>
23  */
24  /* global -ngRouteModule */
25 var ngRouteModule = angular.module('ngRoute', ['ng']).
26                         provider('$route', $RouteProvider);
27
28 /**
29  * @ngdoc object
30  * @name ngRoute.$routeProvider
31  * @function
32  *
33  * @description
34  *
35  * Used for configuring routes.
36  * 
37  * ## Example
38  * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
39  *
40  * ## Dependencies
41  * Requires the {@link ngRoute `ngRoute`} module to be installed.
42  */
43 function $RouteProvider(){
44   function inherit(parent, extra) {
45     return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
46   }
47
48   var routes = {};
49
50   /**
51    * @ngdoc method
52    * @name ngRoute.$routeProvider#when
53    * @methodOf ngRoute.$routeProvider
54    *
55    * @param {string} path Route path (matched against `$location.path`). If `$location.path`
56    *    contains redundant trailing slash or is missing one, the route will still match and the
57    *    `$location.path` will be updated to add or drop the trailing slash to exactly match the
58    *    route definition.
59    *
60    *      * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
61    *        to the next slash are matched and stored in `$routeParams` under the given `name`
62    *        when the route matches.
63    *      * `path` can contain named groups starting with a colon and ending with a star:
64    *        e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
65    *        when the route matches.
66    *      * `path` can contain optional named groups with a question mark: e.g.`:name?`.
67    *
68    *    For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
69    *    `/color/brown/largecode/code/with/slashs/edit` and extract:
70    *
71    *      * `color: brown`
72    *      * `largecode: code/with/slashs`.
73    *
74    *
75    * @param {Object} route Mapping information to be assigned to `$route.current` on route
76    *    match.
77    *
78    *    Object properties:
79    *
80    *    - `controller` – `{(string|function()=}` – Controller fn that should be associated with
81    *      newly created scope or the name of a {@link angular.Module#controller registered
82    *      controller} if passed as a string.
83    *    - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
84    *      published to scope under the `controllerAs` name.
85    *    - `template` – `{string=|function()=}` – html template as a string or a function that
86    *      returns an html template as a string which should be used by {@link
87    *      ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
88    *      This property takes precedence over `templateUrl`.
89    *
90    *      If `template` is a function, it will be called with the following parameters:
91    *
92    *      - `{Array.<Object>}` - route parameters extracted from the current
93    *        `$location.path()` by applying the current route
94    *
95    *    - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
96    *      template that should be used by {@link ngRoute.directive:ngView ngView}.
97    *
98    *      If `templateUrl` is a function, it will be called with the following parameters:
99    *
100    *      - `{Array.<Object>}` - route parameters extracted from the current
101    *        `$location.path()` by applying the current route
102    *
103    *    - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
104    *      be injected into the controller. If any of these dependencies are promises, the router
105    *      will wait for them all to be resolved or one to be rejected before the controller is
106    *      instantiated.
107    *      If all the promises are resolved successfully, the values of the resolved promises are
108    *      injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
109    *      fired. If any of the promises are rejected the
110    *      {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
111    *      is:
112    *
113    *      - `key` – `{string}`: a name of a dependency to be injected into the controller.
114    *      - `factory` - `{string|function}`: If `string` then it is an alias for a service.
115    *        Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
116    *        and the return value is treated as the dependency. If the result is a promise, it is
117    *        resolved before its value is injected into the controller. Be aware that
118    *        `ngRoute.$routeParams` will still refer to the previous route within these resolve
119    *        functions.  Use `$route.current.params` to access the new route parameters, instead.
120    *
121    *    - `redirectTo` – {(string|function())=} – value to update
122    *      {@link ng.$location $location} path with and trigger route redirection.
123    *
124    *      If `redirectTo` is a function, it will be called with the following parameters:
125    *
126    *      - `{Object.<string>}` - route parameters extracted from the current
127    *        `$location.path()` by applying the current route templateUrl.
128    *      - `{string}` - current `$location.path()`
129    *      - `{Object}` - current `$location.search()`
130    *
131    *      The custom `redirectTo` function is expected to return a string which will be used
132    *      to update `$location.path()` and `$location.search()`.
133    *
134    *    - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
135    *      or `$location.hash()` changes.
136    *
137    *      If the option is set to `false` and url in the browser changes, then
138    *      `$routeUpdate` event is broadcasted on the root scope.
139    *
140    *    - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
141    *
142    *      If the option is set to `true`, then the particular route can be matched without being
143    *      case sensitive
144    *
145    * @returns {Object} self
146    *
147    * @description
148    * Adds a new route definition to the `$route` service.
149    */
150   this.when = function(path, route) {
151     routes[path] = angular.extend(
152       {reloadOnSearch: true},
153       route,
154       path && pathRegExp(path, route)
155     );
156
157     // create redirection for trailing slashes
158     if (path) {
159       var redirectPath = (path[path.length-1] == '/')
160             ? path.substr(0, path.length-1)
161             : path +'/';
162
163       routes[redirectPath] = angular.extend(
164         {redirectTo: path},
165         pathRegExp(redirectPath, route)
166       );
167     }
168
169     return this;
170   };
171
172    /**
173     * @param path {string} path
174     * @param opts {Object} options
175     * @return {?Object}
176     *
177     * @description
178     * Normalizes the given path, returning a regular expression
179     * and the original path.
180     *
181     * Inspired by pathRexp in visionmedia/express/lib/utils.js.
182     */
183   function pathRegExp(path, opts) {
184     var insensitive = opts.caseInsensitiveMatch,
185         ret = {
186           originalPath: path,
187           regexp: path
188         },
189         keys = ret.keys = [];
190
191     path = path
192       .replace(/([().])/g, '\\$1')
193       .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option){
194         var optional = option === '?' ? option : null;
195         var star = option === '*' ? option : null;
196         keys.push({ name: key, optional: !!optional });
197         slash = slash || '';
198         return ''
199           + (optional ? '' : slash)
200           + '(?:'
201           + (optional ? slash : '')
202           + (star && '(.+?)' || '([^/]+)')
203           + (optional || '')
204           + ')'
205           + (optional || '');
206       })
207       .replace(/([\/$\*])/g, '\\$1');
208
209     ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
210     return ret;
211   }
212
213   /**
214    * @ngdoc method
215    * @name ngRoute.$routeProvider#otherwise
216    * @methodOf ngRoute.$routeProvider
217    *
218    * @description
219    * Sets route definition that will be used on route change when no other route definition
220    * is matched.
221    *
222    * @param {Object} params Mapping information to be assigned to `$route.current`.
223    * @returns {Object} self
224    */
225   this.otherwise = function(params) {
226     this.when(null, params);
227     return this;
228   };
229
230
231   this.$get = ['$rootScope',
232                '$location',
233                '$routeParams',
234                '$q',
235                '$injector',
236                '$http',
237                '$templateCache',
238                '$sce',
239       function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
240
241     /**
242      * @ngdoc object
243      * @name ngRoute.$route
244      * @requires $location
245      * @requires $routeParams
246      *
247      * @property {Object} current Reference to the current route definition.
248      * The route definition contains:
249      *
250      *   - `controller`: The controller constructor as define in route definition.
251      *   - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
252      *     controller instantiation. The `locals` contain
253      *     the resolved values of the `resolve` map. Additionally the `locals` also contain:
254      *
255      *     - `$scope` - The current route scope.
256      *     - `$template` - The current route template HTML.
257      *
258      * @property {Array.<Object>} routes Array of all configured routes.
259      *
260      * @description
261      * `$route` is used for deep-linking URLs to controllers and views (HTML partials).
262      * It watches `$location.url()` and tries to map the path to an existing route definition.
263      *
264      * Requires the {@link ngRoute `ngRoute`} module to be installed.
265      *
266      * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
267      *
268      * The `$route` service is typically used in conjunction with the
269      * {@link ngRoute.directive:ngView `ngView`} directive and the
270      * {@link ngRoute.$routeParams `$routeParams`} service.
271      *
272      * @example
273        This example shows how changing the URL hash causes the `$route` to match a route against the
274        URL, and the `ngView` pulls in the partial.
275
276        Note that this example is using {@link ng.directive:script inlined templates}
277        to get it working on jsfiddle as well.
278
279      <example module="ngViewExample" deps="angular-route.js">
280        <file name="index.html">
281          <div ng-controller="MainCntl">
282            Choose:
283            <a href="Book/Moby">Moby</a> |
284            <a href="Book/Moby/ch/1">Moby: Ch1</a> |
285            <a href="Book/Gatsby">Gatsby</a> |
286            <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
287            <a href="Book/Scarlet">Scarlet Letter</a><br/>
288
289            <div ng-view></div>
290            <hr />
291
292            <pre>$location.path() = {{$location.path()}}</pre>
293            <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
294            <pre>$route.current.params = {{$route.current.params}}</pre>
295            <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
296            <pre>$routeParams = {{$routeParams}}</pre>
297          </div>
298        </file>
299
300        <file name="book.html">
301          controller: {{name}}<br />
302          Book Id: {{params.bookId}}<br />
303        </file>
304
305        <file name="chapter.html">
306          controller: {{name}}<br />
307          Book Id: {{params.bookId}}<br />
308          Chapter Id: {{params.chapterId}}
309        </file>
310
311        <file name="script.js">
312          angular.module('ngViewExample', ['ngRoute'])
313
314          .config(function($routeProvider, $locationProvider) {
315            $routeProvider.when('/Book/:bookId', {
316              templateUrl: 'book.html',
317              controller: BookCntl,
318              resolve: {
319                // I will cause a 1 second delay
320                delay: function($q, $timeout) {
321                  var delay = $q.defer();
322                  $timeout(delay.resolve, 1000);
323                  return delay.promise;
324                }
325              }
326            });
327            $routeProvider.when('/Book/:bookId/ch/:chapterId', {
328              templateUrl: 'chapter.html',
329              controller: ChapterCntl
330            });
331
332            // configure html5 to get links working on jsfiddle
333            $locationProvider.html5Mode(true);
334          });
335
336          function MainCntl($scope, $route, $routeParams, $location) {
337            $scope.$route = $route;
338            $scope.$location = $location;
339            $scope.$routeParams = $routeParams;
340          }
341
342          function BookCntl($scope, $routeParams) {
343            $scope.name = "BookCntl";
344            $scope.params = $routeParams;
345          }
346
347          function ChapterCntl($scope, $routeParams) {
348            $scope.name = "ChapterCntl";
349            $scope.params = $routeParams;
350          }
351        </file>
352
353        <file name="protractorTest.js">
354          it('should load and compile correct template', function() {
355            element(by.linkText('Moby: Ch1')).click();
356            var content = element(by.css('.doc-example-live [ng-view]')).getText();
357            expect(content).toMatch(/controller\: ChapterCntl/);
358            expect(content).toMatch(/Book Id\: Moby/);
359            expect(content).toMatch(/Chapter Id\: 1/);
360
361            element(by.partialLinkText('Scarlet')).click();
362
363            content = element(by.css('.doc-example-live [ng-view]')).getText();
364            expect(content).toMatch(/controller\: BookCntl/);
365            expect(content).toMatch(/Book Id\: Scarlet/);
366          });
367        </file>
368      </example>
369      */
370
371     /**
372      * @ngdoc event
373      * @name ngRoute.$route#$routeChangeStart
374      * @eventOf ngRoute.$route
375      * @eventType broadcast on root scope
376      * @description
377      * Broadcasted before a route change. At this  point the route services starts
378      * resolving all of the dependencies needed for the route change to occur.
379      * Typically this involves fetching the view template as well as any dependencies
380      * defined in `resolve` route property. Once  all of the dependencies are resolved
381      * `$routeChangeSuccess` is fired.
382      *
383      * @param {Object} angularEvent Synthetic event object.
384      * @param {Route} next Future route information.
385      * @param {Route} current Current route information.
386      */
387
388     /**
389      * @ngdoc event
390      * @name ngRoute.$route#$routeChangeSuccess
391      * @eventOf ngRoute.$route
392      * @eventType broadcast on root scope
393      * @description
394      * Broadcasted after a route dependencies are resolved.
395      * {@link ngRoute.directive:ngView ngView} listens for the directive
396      * to instantiate the controller and render the view.
397      *
398      * @param {Object} angularEvent Synthetic event object.
399      * @param {Route} current Current route information.
400      * @param {Route|Undefined} previous Previous route information, or undefined if current is
401      * first route entered.
402      */
403
404     /**
405      * @ngdoc event
406      * @name ngRoute.$route#$routeChangeError
407      * @eventOf ngRoute.$route
408      * @eventType broadcast on root scope
409      * @description
410      * Broadcasted if any of the resolve promises are rejected.
411      *
412      * @param {Object} angularEvent Synthetic event object
413      * @param {Route} current Current route information.
414      * @param {Route} previous Previous route information.
415      * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
416      */
417
418     /**
419      * @ngdoc event
420      * @name ngRoute.$route#$routeUpdate
421      * @eventOf ngRoute.$route
422      * @eventType broadcast on root scope
423      * @description
424      *
425      * The `reloadOnSearch` property has been set to false, and we are reusing the same
426      * instance of the Controller.
427      */
428
429     var forceReload = false,
430         $route = {
431           routes: routes,
432
433           /**
434            * @ngdoc method
435            * @name ngRoute.$route#reload
436            * @methodOf ngRoute.$route
437            *
438            * @description
439            * Causes `$route` service to reload the current route even if
440            * {@link ng.$location $location} hasn't changed.
441            *
442            * As a result of that, {@link ngRoute.directive:ngView ngView}
443            * creates new scope, reinstantiates the controller.
444            */
445           reload: function() {
446             forceReload = true;
447             $rootScope.$evalAsync(updateRoute);
448           }
449         };
450
451     $rootScope.$on('$locationChangeSuccess', updateRoute);
452
453     return $route;
454
455     /////////////////////////////////////////////////////
456
457     /**
458      * @param on {string} current url
459      * @param route {Object} route regexp to match the url against
460      * @return {?Object}
461      *
462      * @description
463      * Check if the route matches the current url.
464      *
465      * Inspired by match in
466      * visionmedia/express/lib/router/router.js.
467      */
468     function switchRouteMatcher(on, route) {
469       var keys = route.keys,
470           params = {};
471
472       if (!route.regexp) return null;
473
474       var m = route.regexp.exec(on);
475       if (!m) return null;
476
477       for (var i = 1, len = m.length; i < len; ++i) {
478         var key = keys[i - 1];
479
480         var val = 'string' == typeof m[i]
481               ? decodeURIComponent(m[i])
482               : m[i];
483
484         if (key && val) {
485           params[key.name] = val;
486         }
487       }
488       return params;
489     }
490
491     function updateRoute() {
492       var next = parseRoute(),
493           last = $route.current;
494
495       if (next && last && next.$$route === last.$$route
496           && angular.equals(next.pathParams, last.pathParams)
497           && !next.reloadOnSearch && !forceReload) {
498         last.params = next.params;
499         angular.copy(last.params, $routeParams);
500         $rootScope.$broadcast('$routeUpdate', last);
501       } else if (next || last) {
502         forceReload = false;
503         $rootScope.$broadcast('$routeChangeStart', next, last);
504         $route.current = next;
505         if (next) {
506           if (next.redirectTo) {
507             if (angular.isString(next.redirectTo)) {
508               $location.path(interpolate(next.redirectTo, next.params)).search(next.params)
509                        .replace();
510             } else {
511               $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
512                        .replace();
513             }
514           }
515         }
516
517         $q.when(next).
518           then(function() {
519             if (next) {
520               var locals = angular.extend({}, next.resolve),
521                   template, templateUrl;
522
523               angular.forEach(locals, function(value, key) {
524                 locals[key] = angular.isString(value) ?
525                     $injector.get(value) : $injector.invoke(value);
526               });
527
528               if (angular.isDefined(template = next.template)) {
529                 if (angular.isFunction(template)) {
530                   template = template(next.params);
531                 }
532               } else if (angular.isDefined(templateUrl = next.templateUrl)) {
533                 if (angular.isFunction(templateUrl)) {
534                   templateUrl = templateUrl(next.params);
535                 }
536                 templateUrl = $sce.getTrustedResourceUrl(templateUrl);
537                 if (angular.isDefined(templateUrl)) {
538                   next.loadedTemplateUrl = templateUrl;
539                   template = $http.get(templateUrl, {cache: $templateCache}).
540                       then(function(response) { return response.data; });
541                 }
542               }
543               if (angular.isDefined(template)) {
544                 locals['$template'] = template;
545               }
546               return $q.all(locals);
547             }
548           }).
549           // after route change
550           then(function(locals) {
551             if (next == $route.current) {
552               if (next) {
553                 next.locals = locals;
554                 angular.copy(next.params, $routeParams);
555               }
556               $rootScope.$broadcast('$routeChangeSuccess', next, last);
557             }
558           }, function(error) {
559             if (next == $route.current) {
560               $rootScope.$broadcast('$routeChangeError', next, last, error);
561             }
562           });
563       }
564     }
565
566
567     /**
568      * @returns the current active route, by matching it against the URL
569      */
570     function parseRoute() {
571       // Match a route
572       var params, match;
573       angular.forEach(routes, function(route, path) {
574         if (!match && (params = switchRouteMatcher($location.path(), route))) {
575           match = inherit(route, {
576             params: angular.extend({}, $location.search(), params),
577             pathParams: params});
578           match.$$route = route;
579         }
580       });
581       // No route matched; fallback to "otherwise" route
582       return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
583     }
584
585     /**
586      * @returns interpolation of the redirect path with the parameters
587      */
588     function interpolate(string, params) {
589       var result = [];
590       angular.forEach((string||'').split(':'), function(segment, i) {
591         if (i === 0) {
592           result.push(segment);
593         } else {
594           var segmentMatch = segment.match(/(\w+)(.*)/);
595           var key = segmentMatch[1];
596           result.push(params[key]);
597           result.push(segmentMatch[2] || '');
598           delete params[key];
599         }
600       });
601       return result.join('');
602     }
603   }];
604 }
605
606 ngRouteModule.provider('$routeParams', $RouteParamsProvider);
607
608
609 /**
610  * @ngdoc object
611  * @name ngRoute.$routeParams
612  * @requires $route
613  *
614  * @description
615  * The `$routeParams` service allows you to retrieve the current set of route parameters.
616  *
617  * Requires the {@link ngRoute `ngRoute`} module to be installed.
618  *
619  * The route parameters are a combination of {@link ng.$location `$location`}'s
620  * {@link ng.$location#methods_search `search()`} and {@link ng.$location#methods_path `path()`}.
621  * The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
622  *
623  * In case of parameter name collision, `path` params take precedence over `search` params.
624  *
625  * The service guarantees that the identity of the `$routeParams` object will remain unchanged
626  * (but its properties will likely change) even when a route change occurs.
627  *
628  * Note that the `$routeParams` are only updated *after* a route change completes successfully.
629  * This means that you cannot rely on `$routeParams` being correct in route resolve functions.
630  * Instead you can use `$route.current.params` to access the new route's parameters.
631  *
632  * @example
633  * <pre>
634  *  // Given:
635  *  // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
636  *  // Route: /Chapter/:chapterId/Section/:sectionId
637  *  //
638  *  // Then
639  *  $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
640  * </pre>
641  */
642 function $RouteParamsProvider() {
643   this.$get = function() { return {}; };
644 }
645
646 ngRouteModule.directive('ngView', ngViewFactory);
647 ngRouteModule.directive('ngView', ngViewFillContentFactory);
648
649
650 /**
651  * @ngdoc directive
652  * @name ngRoute.directive:ngView
653  * @restrict ECA
654  *
655  * @description
656  * # Overview
657  * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
658  * including the rendered template of the current route into the main layout (`index.html`) file.
659  * Every time the current route changes, the included view changes with it according to the
660  * configuration of the `$route` service.
661  *
662  * Requires the {@link ngRoute `ngRoute`} module to be installed.
663  *
664  * @animations
665  * enter - animation is used to bring new content into the browser.
666  * leave - animation is used to animate existing content away.
667  *
668  * The enter and leave animation occur concurrently.
669  *
670  * @scope
671  * @priority 400
672  * @param {string=} onload Expression to evaluate whenever the view updates.
673  *
674  * @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
675  *                  $anchorScroll} to scroll the viewport after the view is updated.
676  *
677  *                  - If the attribute is not set, disable scrolling.
678  *                  - If the attribute is set without value, enable scrolling.
679  *                  - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
680  *                    as an expression yields a truthy value.
681  * @example
682     <example module="ngViewExample" deps="angular-route.js" animations="true">
683       <file name="index.html">
684         <div ng-controller="MainCntl as main">
685           Choose:
686           <a href="Book/Moby">Moby</a> |
687           <a href="Book/Moby/ch/1">Moby: Ch1</a> |
688           <a href="Book/Gatsby">Gatsby</a> |
689           <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
690           <a href="Book/Scarlet">Scarlet Letter</a><br/>
691
692           <div class="view-animate-container">
693             <div ng-view class="view-animate"></div>
694           </div>
695           <hr />
696
697           <pre>$location.path() = {{main.$location.path()}}</pre>
698           <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
699           <pre>$route.current.params = {{main.$route.current.params}}</pre>
700           <pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
701           <pre>$routeParams = {{main.$routeParams}}</pre>
702         </div>
703       </file>
704
705       <file name="book.html">
706         <div>
707           controller: {{book.name}}<br />
708           Book Id: {{book.params.bookId}}<br />
709         </div>
710       </file>
711
712       <file name="chapter.html">
713         <div>
714           controller: {{chapter.name}}<br />
715           Book Id: {{chapter.params.bookId}}<br />
716           Chapter Id: {{chapter.params.chapterId}}
717         </div>
718       </file>
719
720       <file name="animations.css">
721         .view-animate-container {
722           position:relative;
723           height:100px!important;
724           position:relative;
725           background:white;
726           border:1px solid black;
727           height:40px;
728           overflow:hidden;
729         }
730
731         .view-animate {
732           padding:10px;
733         }
734
735         .view-animate.ng-enter, .view-animate.ng-leave {
736           -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
737           transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
738
739           display:block;
740           width:100%;
741           border-left:1px solid black;
742
743           position:absolute;
744           top:0;
745           left:0;
746           right:0;
747           bottom:0;
748           padding:10px;
749         }
750
751         .view-animate.ng-enter {
752           left:100%;
753         }
754         .view-animate.ng-enter.ng-enter-active {
755           left:0;
756         }
757         .view-animate.ng-leave.ng-leave-active {
758           left:-100%;
759         }
760       </file>
761
762       <file name="script.js">
763         angular.module('ngViewExample', ['ngRoute', 'ngAnimate'],
764           function($routeProvider, $locationProvider) {
765             $routeProvider.when('/Book/:bookId', {
766               templateUrl: 'book.html',
767               controller: BookCntl,
768               controllerAs: 'book'
769             });
770             $routeProvider.when('/Book/:bookId/ch/:chapterId', {
771               templateUrl: 'chapter.html',
772               controller: ChapterCntl,
773               controllerAs: 'chapter'
774             });
775
776             // configure html5 to get links working on jsfiddle
777             $locationProvider.html5Mode(true);
778         });
779
780         function MainCntl($route, $routeParams, $location) {
781           this.$route = $route;
782           this.$location = $location;
783           this.$routeParams = $routeParams;
784         }
785
786         function BookCntl($routeParams) {
787           this.name = "BookCntl";
788           this.params = $routeParams;
789         }
790
791         function ChapterCntl($routeParams) {
792           this.name = "ChapterCntl";
793           this.params = $routeParams;
794         }
795       </file>
796
797       <file name="protractorTest.js">
798         it('should load and compile correct template', function() {
799           element(by.linkText('Moby: Ch1')).click();
800           var content = element(by.css('.doc-example-live [ng-view]')).getText();
801           expect(content).toMatch(/controller\: ChapterCntl/);
802           expect(content).toMatch(/Book Id\: Moby/);
803           expect(content).toMatch(/Chapter Id\: 1/);
804
805           element(by.partialLinkText('Scarlet')).click();
806
807           content = element(by.css('.doc-example-live [ng-view]')).getText();
808           expect(content).toMatch(/controller\: BookCntl/);
809           expect(content).toMatch(/Book Id\: Scarlet/);
810         });
811       </file>
812     </example>
813  */
814
815
816 /**
817  * @ngdoc event
818  * @name ngRoute.directive:ngView#$viewContentLoaded
819  * @eventOf ngRoute.directive:ngView
820  * @eventType emit on the current ngView scope
821  * @description
822  * Emitted every time the ngView content is reloaded.
823  */
824 ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
825 function ngViewFactory(   $route,   $anchorScroll,   $animate) {
826   return {
827     restrict: 'ECA',
828     terminal: true,
829     priority: 400,
830     transclude: 'element',
831     link: function(scope, $element, attr, ctrl, $transclude) {
832         var currentScope,
833             currentElement,
834             autoScrollExp = attr.autoscroll,
835             onloadExp = attr.onload || '';
836
837         scope.$on('$routeChangeSuccess', update);
838         update();
839
840         function cleanupLastView() {
841           if (currentScope) {
842             currentScope.$destroy();
843             currentScope = null;
844           }
845           if(currentElement) {
846             $animate.leave(currentElement);
847             currentElement = null;
848           }
849         }
850
851         function update() {
852           var locals = $route.current && $route.current.locals,
853               template = locals && locals.$template;
854
855           if (angular.isDefined(template)) {
856             var newScope = scope.$new();
857             var current = $route.current;
858
859             // Note: This will also link all children of ng-view that were contained in the original
860             // html. If that content contains controllers, ... they could pollute/change the scope.
861             // However, using ng-view on an element with additional content does not make sense...
862             // Note: We can't remove them in the cloneAttchFn of $transclude as that
863             // function is called before linking the content, which would apply child
864             // directives to non existing elements.
865             var clone = $transclude(newScope, function(clone) {
866               $animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
867                 if (angular.isDefined(autoScrollExp)
868                   && (!autoScrollExp || scope.$eval(autoScrollExp))) {
869                   $anchorScroll();
870                 }
871               });
872               cleanupLastView();
873             });
874
875             currentElement = clone;
876             currentScope = current.scope = newScope;
877             currentScope.$emit('$viewContentLoaded');
878             currentScope.$eval(onloadExp);
879           } else {
880             cleanupLastView();
881           }
882         }
883     }
884   };
885 }
886
887 // This directive is called during the $transclude call of the first `ngView` directive.
888 // It will replace and compile the content of the element with the loaded template.
889 // We need this directive so that the element content is already filled when
890 // the link function of another directive on the same element as ngView
891 // is called.
892 ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
893 function ngViewFillContentFactory($compile, $controller, $route) {
894   return {
895     restrict: 'ECA',
896     priority: -400,
897     link: function(scope, $element) {
898       var current = $route.current,
899           locals = current.locals;
900
901       $element.html(locals.$template);
902
903       var link = $compile($element.contents());
904
905       if (current.controller) {
906         locals.$scope = scope;
907         var controller = $controller(current.controller, locals);
908         if (current.controllerAs) {
909           scope[current.controllerAs] = controller;
910         }
911         $element.data('$ngControllerController', controller);
912         $element.children().data('$ngControllerController', controller);
913       }
914
915       link(scope);
916     }
917   };
918 }
919
920
921 })(window, window.angular);