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