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