angular-visor
Advanced tools
Comparing version 0.1.1 to 0.1.2
{ | ||
"name": "visor", | ||
"description": "Angular authentication and authorization library", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"homepage": "https://github.com/illniyar/visor", | ||
"main": "./release/visor.js", | ||
"dependencies": { | ||
"angular": "~1.3.0" | ||
"angular": ">= 1.2.0" | ||
}, | ||
"devDependencies": { | ||
"angular-ui-router": "~0.2.13", | ||
"angular-mocks": "~1.3.0", | ||
"angular-route": "~1.3.0", | ||
"angular-cookies": "~1.3.0" | ||
"angular-ui-router": ">= 0.2.13", | ||
"angular-mocks": ">= 1.2.0", | ||
"angular-route": ">= 1.2.0", | ||
"angular-cookies": ">= 1.2.0" | ||
}, | ||
@@ -16,0 +16,0 @@ "ignore": [ |
@@ -1,9 +0,26 @@ | ||
<a name"0.1.1"></a> | ||
### 0.1.1 (2015-07-08) | ||
<a name"0.1.2"></a> | ||
### 0.1.2 (2015-12-07) | ||
#### Bug Fixes | ||
* **visor:** next parameter now overrides existing parameters ([b0c89109](https://github.com/illniyar/visor/commit/b0c89109), closes [#14](https://github.com/illniyar/visor/issues/14)) | ||
#### Features | ||
* **visor:** allow changing next parameter name ([04d5d5d0](https://github.com/illniyar/visor/commit/04d5d5d0)) | ||
<a name"0.1.0"></a> | ||
## 0.1.0 (2015-07-08) | ||
### 0.1.0 (2015-07-08) | ||
#### Bug Fixes | ||
* **ui-router:** state inheritance with dot notation does work with two levels of inheritance ([b7b05d5fce7dafd9f1047dc1e01ec2f4fd483ed5](https://github.com/illniyar/visor/commit/b7b05d5fce7dafd9f1047dc1e01ec2f4fd483ed5)) | ||
#### Features | ||
* **visor.allowed:** add directives to add/remove classes from an element based on route restriction ([84f443bda058a6a950470899cd1331342ae05c69](https://github.com/illniyar/visor/commit/84f443bda058a6a950470899cd1331342ae05c69)) | ||
<a name"0.0.6"></a> | ||
@@ -10,0 +27,0 @@ ### 0.0.6 (2015-06-17) |
@@ -74,3 +74,11 @@ module.exports = function (grunt) { | ||
dest: '<%=releasedir%>/' | ||
}] | ||
}, | ||
{src: 'bower_components/angular/angular.js', | ||
dest:'sample/lib/angular.js'}, | ||
{src: 'bower_components/angular-ui-router/release/angular-ui-router.js', | ||
dest:'sample/lib/angular-ui-router.js'}, | ||
{src: 'bower_components/angular-route/angular-route.js', | ||
dest:'sample/lib/angular-route.js'}, | ||
{src: 'bower_components/angular-cookies/angular-cookies.js', | ||
dest:'sample/lib/angular-cookies.js'}] | ||
}, | ||
@@ -162,6 +170,6 @@ site: { | ||
grunt.registerTask('site', 'Build and create site', ['dist', 'copy:site', 'ngdocs:all']); | ||
grunt.registerTask('gh-pages', 'Build, create site and push to gh-pages', ['gh-pages', 'clean:gh-pages']); | ||
grunt.registerTask('build-gh-pages', 'Build, create site and push to gh-pages', ['gh-pages', 'clean:gh-pages']); | ||
grunt.registerTask('push-to-git', 'Add, commit, create tag and push to git', ['gitadd:release', 'gitcommit:master', 'gittag:release', 'gitpush:origin']); | ||
grunt.registerTask('publish', 'Builds and publishes to all relevent repositories', | ||
['bumpup:patch', 'site', 'changelog', 'push-to-git', 'npm-publish']) | ||
['bumpup:patch', 'site', 'changelog', 'push-to-git', 'npm-publish','build-gh-pages']) | ||
} |
{ | ||
"name": "angular-visor", | ||
"author": "Illniyar", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"main": "release/visor.js", | ||
@@ -42,4 +42,4 @@ "description": "Angular authentication and authorization library", | ||
"dependencies": { | ||
"angular": "^1.3.16" | ||
"angular": ">= 1.2.0" | ||
} | ||
} |
/**visor | ||
* Angular authentication and authorization library | ||
* @version v0.1.1 | ||
* @version v0.1.2 | ||
* @link https://github.com/illniyar/visor.git | ||
@@ -319,13 +319,4 @@ * @license MIT License, http://www.opensource.org/licenses/MIT | ||
.provider("visor", [function () { | ||
function addNextToUrl(url, $location, restrictedUrl) { | ||
if (config.shouldAddNext) { | ||
if (url.indexOf("?") >= 0) { | ||
return url.replace(/\?/, "?next=" + encodeURIComponent(restrictedUrl) + "&"); | ||
} | ||
return url + "?next=" + encodeURIComponent(restrictedUrl); | ||
} else { | ||
return url; | ||
} | ||
} | ||
var config = this; | ||
@@ -399,4 +390,18 @@ /** | ||
config.shouldAddNext = true; | ||
/** | ||
* @ngdoc property | ||
* @name visor.visorProvider#nextParameterName | ||
* @propertyOf visor.visorProvider | ||
* | ||
* @description | ||
* | ||
* The name of the parameter where the url that was restricted will be added to determine redirections | ||
* when using {@link visor.visorProvider#shouldAddNext `visorProvider.shouldAddNext`} | ||
* | ||
* See {@link visor.visorProvider#shouldAddNext `visorProvider.shouldAddNext`} for more. | ||
* | ||
* Defaults to 'next' | ||
*/ | ||
config.nextParameterName = 'next'; | ||
/** | ||
* @ngdoc function | ||
@@ -459,3 +464,6 @@ * @name visor.visorProvider#authenticate | ||
config.doOnNotAuthenticated = ["$location", "restrictedUrl", function ($location, restrictedUrl) { | ||
$location.url(addNextToUrl(config.loginRoute, $location, restrictedUrl)) | ||
$location.url(config.loginRoute); | ||
if (config.shouldAddNext) { | ||
$location.search(config.nextParameterName,restrictedUrl); | ||
} | ||
}]; | ||
@@ -485,3 +493,3 @@ /** | ||
config.doAfterManualAuthentication = ["$location", function ($location) { | ||
$location.url($location.search().next || config.homeRoute); | ||
$location.url($location.search()[config.nextParameterName] || config.homeRoute); | ||
}]; | ||
@@ -488,0 +496,0 @@ /** |
/**visor | ||
* Angular authentication and authorization library | ||
* @version v0.1.1 | ||
* @version v0.1.2 | ||
* @link https://github.com/illniyar/visor.git | ||
@@ -8,2 +8,2 @@ * @license MIT License, http://www.opensource.org/licenses/MIT | ||
"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="visor"),function(a,b,c){!function(){"use strict";b.module("delayLocationChange",[]).service("delayLocationChange",["$rootScope","$q","$timeout","$location","$injector",function(a,b,c,d,e){function f(){m--,o&&0>=m&&g()}function g(){d.absUrl()===i?a.$broadcast("$locationChangeSuccess",i,j):d.url(k)}function h(a){m++,a["finally"](f)}var i,j,k,l=function(a){a.then?h(a):o?h(e.invoke(fn)):n.push(a)},m=0,n=[],o=!1,p=a.$on("$locationChangeStart",function(a,b,g){o=!0,k=d.url(),p(),a.preventDefault(),n.forEach(function(a){h(e.invoke(a))}),0!==m||i||(m++,c(f,1)),i=b,j=g});return l}])}(),function(){"use strict";b.module("visor.allowed",["visor.permissions"]).directive("showIfAllowed",["visorPermissions","$animate",function(a,b){return{restrict:"A",link:function(c,d,e){function f(c){var e=a.checkPermissionsForRoute(c);b[e?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})}var g=a.notifyOnCacheClear(function(){f(e.showIfAllowed)});e.$observe("showIfAllowed",f),c.$on("$destroy",g)}}}]).directive("classIfRestricted",["visorPermissions","$animate",function(a,b){return{restrict:"A",link:function(c,d,e){function f(c){var f=a.checkPermissionsForRoute(c);b[f?"removeClass":"addClass"](d,e.restrictedClass||"visor-restricted")}var g=a.notifyOnCacheClear(function(){f(e.classIfRestricted)});e.$observe("classIfRestricted",f),c.$on("$destroy",g)}}}])}(),function(){"use strict";b.module("visor",["visor.permissions","visor.ui-router","visor.ngRoute","delayLocationChange","visor.allowed"]).constant("authenticatedOnly",function(a){return!!a}).constant("notForAuthenticated",function(a){return a===c}).provider("visor",[function(){function a(a,c,d){return b.shouldAddNext?a.indexOf("?")>=0?a.replace(/\?/,"?next="+encodeURIComponent(d)+"&"):a+"?next="+encodeURIComponent(d):a}var b=this;b.authenticateOnStartup=!0,b.loginRoute="/login",b.homeRoute="/",b.notAuthorizedRoute="/access_denied",b.shouldAddNext=!0,b.authenticate=function(){throw new Error("visorProvider.authenticate must be defined to use visor")},b.doOnNotAuthenticated=["$location","restrictedUrl",function(c,d){c.url(a(b.loginRoute,c,d))}],b.doAfterManualAuthentication=["$location",function(a){a.url(a.search().next||b.homeRoute)}],b.doOnNotAuthorized=["$location",function(a){a.url(b.notAuthorizedRoute)}],this.$get=["$injector","$q","$rootScope","$location","visorPermissions",function(a,d,e,f,g){function h(a){k.authData=a,g.invokeParameters=[k.authData],g.clearPermissionCache()}function i(){k.authData=c,g.invokeParameters=[],g.clearPermissionCache()}var j=!1,k={authenticate:function(c){if(j&&!c)return j;var e=d.defer();return j=e.promise,a.invoke(b.authenticate).then(h,i)["finally"](function(){e.resolve(k.authData)}),e.promise},setAuthenticated:function(c){h(c),j=d.when(c),a.invoke(b.doAfterManualAuthentication,null,{authData:c})},isAuthenticated:function(){return!!k.authData},onNotAllowed:function(c){k.isAuthenticated()?a.invoke(b.doOnNotAuthorized,null,{restrictedUrl:c}):a.invoke(b.doOnNotAuthenticated,null,{restrictedUrl:c})},setUnauthenticated:function(){i()},config:b};return k}]}]).run(["visor","delayLocationChange",function(a,b){a.config.authenticateOnStartup&&b(a.authenticate())}]).config(["visorPermissionsProvider",function(a){a.doBeforeFirstCheck.push(["visor",function(a){return a.authenticate()}]),a.onNotAllowed=["visor","restrictedUrl",function(a,b){a.onNotAllowed(b)}]}])}(),function(){b.module("visor.ngRoute",["visor.permissions"]).run(["$rootScope","visorPermissions","$injector",function(a,b,c){var d=!1,e=null;try{e=c.get("$route"),d=!0}catch(f){}d&&(b.getRoute=function(a){for(var b in e.routes){var c=e.routes[b];if(c.regexp.exec(a))return c}return null},a.$on("$routeChangeStart",function(a,c){c.resolve=c.resolve||{},b.onRouteChange(c,function(a){c.resolve._visorDelay=function(){return a}})}))}])}(),function(){b.module("visor.permissions",[]).provider("visorPermissions",[function(){var a=this;a.getPermissionsFromNext=function(a){return a.restrict?[a.restrict]:[]},a.doBeforeFirstCheck=[],a.onNotAllowed=function(){},a.invokeParameters=[],a.getRoute=function(a){throw new Error("method not implemented")};var d=!1;this.$get=["$q","$injector","$location",function(e,f,g){function h(a){if(!a||0===a.length)return!0;b.isArray(a)||(a=[a]);var c=!0;return a.forEach(function(a){c=c&&a.apply(null,l.invokeParameters)}),c}function i(b,c){var d=h(c);return d?!0:(l.invokeNotAllowed(a.onNotAllowed),!1)}var j=[],k={},l={onRouteChange:function(b,c){var g=l.getPermissionsFromNext(b);if(!g||0==g.length)return!0;if(d)return i(b,g);var h=e.defer();return c(h.promise),e.all(a.doBeforeFirstCheck.map(function(a){return f.invoke(a)}))["finally"](function(){d=!0,i(b,g)?h.resolve(!0):h.reject(!1)}),"delayed"},getPermissionsFromNext:a.getPermissionsFromNext,checkPermissionsForRoute:function(a){var b=k[a];if(b!==c)return b;var d=l.getRoute(a);if(!d)return c;var e=l.getPermissionsFromNext(d);return b=h(e),k[a]=b,b},clearPermissionCache:function(){k={},j.forEach(function(a){a&&a()})},notifyOnCacheClear:function(a){return j.push(a),function(){var b=j.indexOf(a);-1!=b&&j.splice(b,1)}},getRoute:a.getRoute,invokeParameters:a.invokeParameters,invokeNotAllowed:function(a){f.invoke(a,null,{restrictedUrl:g.url()})}};return l}]}])}(),function(){b.module("visor.ui-router",["visor.permissions"]).run(["$rootScope","visorPermissions","$injector","$timeout","$location",function(a,b,c,d,e){var f=!1;try{c.get("$state"),f=!0}catch(g){}f&&c.invoke(["$state",function(e){b.getPermissionsFromNext=function(a){for(var b=[];a;)if(a.restrict&&b.unshift(a.restrict),a.parent)a=e.get(a.parent);else if(a.name.indexOf(".")>0){var c=a.name.split(".");c.pop();var d=c.join(".");a=e.get(d)}else a=null;return b};var f=(c.get("$urlRouter"),null),g=!1;a.$on("$stateChangeStart",function(a,c,d){if(g)return void(g=!1);f=e.href(c,d).replace(/^#/,"");var h=b.onRouteChange(c,function(a){a.then(function(){g=!0,e.go(c,d)})});h&&"delayed"!==h||a.preventDefault()}),b.invokeNotAllowed=function(a){d(function(){c.invoke(a,null,{restrictedUrl:f})},0)},b.getRoute=function(a){return e.get(a)}}])}])}()}(window,window.angular); | ||
"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="visor"),function(a,b,c){!function(){"use strict";b.module("delayLocationChange",[]).service("delayLocationChange",["$rootScope","$q","$timeout","$location","$injector",function(a,b,c,d,e){function f(){m--,o&&0>=m&&g()}function g(){d.absUrl()===i?a.$broadcast("$locationChangeSuccess",i,j):d.url(k)}function h(a){m++,a["finally"](f)}var i,j,k,l=function(a){a.then?h(a):o?h(e.invoke(fn)):n.push(a)},m=0,n=[],o=!1,p=a.$on("$locationChangeStart",function(a,b,g){o=!0,k=d.url(),p(),a.preventDefault(),n.forEach(function(a){h(e.invoke(a))}),0!==m||i||(m++,c(f,1)),i=b,j=g});return l}])}(),function(){"use strict";b.module("visor.allowed",["visor.permissions"]).directive("showIfAllowed",["visorPermissions","$animate",function(a,b){return{restrict:"A",link:function(c,d,e){function f(c){var e=a.checkPermissionsForRoute(c);b[e?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})}var g=a.notifyOnCacheClear(function(){f(e.showIfAllowed)});e.$observe("showIfAllowed",f),c.$on("$destroy",g)}}}]).directive("classIfRestricted",["visorPermissions","$animate",function(a,b){return{restrict:"A",link:function(c,d,e){function f(c){var f=a.checkPermissionsForRoute(c);b[f?"removeClass":"addClass"](d,e.restrictedClass||"visor-restricted")}var g=a.notifyOnCacheClear(function(){f(e.classIfRestricted)});e.$observe("classIfRestricted",f),c.$on("$destroy",g)}}}])}(),function(){"use strict";b.module("visor",["visor.permissions","visor.ui-router","visor.ngRoute","delayLocationChange","visor.allowed"]).constant("authenticatedOnly",function(a){return!!a}).constant("notForAuthenticated",function(a){return a===c}).provider("visor",[function(){var a=this;a.authenticateOnStartup=!0,a.loginRoute="/login",a.homeRoute="/",a.notAuthorizedRoute="/access_denied",a.shouldAddNext=!0,a.nextParameterName="next",a.authenticate=function(){throw new Error("visorProvider.authenticate must be defined to use visor")},a.doOnNotAuthenticated=["$location","restrictedUrl",function(b,c){b.url(a.loginRoute),a.shouldAddNext&&b.search(a.nextParameterName,c)}],a.doAfterManualAuthentication=["$location",function(b){b.url(b.search()[a.nextParameterName]||a.homeRoute)}],a.doOnNotAuthorized=["$location",function(b){b.url(a.notAuthorizedRoute)}],this.$get=["$injector","$q","$rootScope","$location","visorPermissions",function(b,d,e,f,g){function h(a){k.authData=a,g.invokeParameters=[k.authData],g.clearPermissionCache()}function i(){k.authData=c,g.invokeParameters=[],g.clearPermissionCache()}var j=!1,k={authenticate:function(c){if(j&&!c)return j;var e=d.defer();return j=e.promise,b.invoke(a.authenticate).then(h,i)["finally"](function(){e.resolve(k.authData)}),e.promise},setAuthenticated:function(c){h(c),j=d.when(c),b.invoke(a.doAfterManualAuthentication,null,{authData:c})},isAuthenticated:function(){return!!k.authData},onNotAllowed:function(c){k.isAuthenticated()?b.invoke(a.doOnNotAuthorized,null,{restrictedUrl:c}):b.invoke(a.doOnNotAuthenticated,null,{restrictedUrl:c})},setUnauthenticated:function(){i()},config:a};return k}]}]).run(["visor","delayLocationChange",function(a,b){a.config.authenticateOnStartup&&b(a.authenticate())}]).config(["visorPermissionsProvider",function(a){a.doBeforeFirstCheck.push(["visor",function(a){return a.authenticate()}]),a.onNotAllowed=["visor","restrictedUrl",function(a,b){a.onNotAllowed(b)}]}])}(),function(){b.module("visor.ngRoute",["visor.permissions"]).run(["$rootScope","visorPermissions","$injector",function(a,b,c){var d=!1,e=null;try{e=c.get("$route"),d=!0}catch(f){}d&&(b.getRoute=function(a){for(var b in e.routes){var c=e.routes[b];if(c.regexp.exec(a))return c}return null},a.$on("$routeChangeStart",function(a,c){c.resolve=c.resolve||{},b.onRouteChange(c,function(a){c.resolve._visorDelay=function(){return a}})}))}])}(),function(){b.module("visor.permissions",[]).provider("visorPermissions",[function(){var a=this;a.getPermissionsFromNext=function(a){return a.restrict?[a.restrict]:[]},a.doBeforeFirstCheck=[],a.onNotAllowed=function(){},a.invokeParameters=[],a.getRoute=function(a){throw new Error("method not implemented")};var d=!1;this.$get=["$q","$injector","$location",function(e,f,g){function h(a){if(!a||0===a.length)return!0;b.isArray(a)||(a=[a]);var c=!0;return a.forEach(function(a){c=c&&a.apply(null,l.invokeParameters)}),c}function i(b,c){var d=h(c);return d?!0:(l.invokeNotAllowed(a.onNotAllowed),!1)}var j=[],k={},l={onRouteChange:function(b,c){var g=l.getPermissionsFromNext(b);if(!g||0==g.length)return!0;if(d)return i(b,g);var h=e.defer();return c(h.promise),e.all(a.doBeforeFirstCheck.map(function(a){return f.invoke(a)}))["finally"](function(){d=!0,i(b,g)?h.resolve(!0):h.reject(!1)}),"delayed"},getPermissionsFromNext:a.getPermissionsFromNext,checkPermissionsForRoute:function(a){var b=k[a];if(b!==c)return b;var d=l.getRoute(a);if(!d)return c;var e=l.getPermissionsFromNext(d);return b=h(e),k[a]=b,b},clearPermissionCache:function(){k={},j.forEach(function(a){a&&a()})},notifyOnCacheClear:function(a){return j.push(a),function(){var b=j.indexOf(a);-1!=b&&j.splice(b,1)}},getRoute:a.getRoute,invokeParameters:a.invokeParameters,invokeNotAllowed:function(a){f.invoke(a,null,{restrictedUrl:g.url()})}};return l}]}])}(),function(){b.module("visor.ui-router",["visor.permissions"]).run(["$rootScope","visorPermissions","$injector","$timeout","$location",function(a,b,c,d,e){var f=!1;try{c.get("$state"),f=!0}catch(g){}f&&c.invoke(["$state",function(e){b.getPermissionsFromNext=function(a){for(var b=[];a;)if(a.restrict&&b.unshift(a.restrict),a.parent)a=e.get(a.parent);else if(a.name.indexOf(".")>0){var c=a.name.split(".");c.pop();var d=c.join(".");a=e.get(d)}else a=null;return b};var f=(c.get("$urlRouter"),null),g=!1;a.$on("$stateChangeStart",function(a,c,d){if(g)return void(g=!1);f=e.href(c,d).replace(/^#/,"");var h=b.onRouteChange(c,function(a){a.then(function(){g=!0,e.go(c,d)})});h&&"delayed"!==h||a.preventDefault()}),b.invokeNotAllowed=function(a){d(function(){c.invoke(a,null,{restrictedUrl:f})},0)},b.getRoute=function(a){return e.get(a)}}])}])}()}(window,window.angular); |
/** | ||
* @license AngularJS v1.3.4 | ||
* (c) 2010-2014 Google, Inc. http://angularjs.org | ||
* @license AngularJS v1.4.8 | ||
* (c) 2010-2015 Google, Inc. http://angularjs.org | ||
* License: MIT | ||
*/ | ||
(function (window, angular, undefined) { | ||
'use strict'; | ||
(function(window, angular, undefined) {'use strict'; | ||
/** | ||
* @ngdoc module | ||
* @name ngCookies | ||
* @description | ||
* | ||
* # ngCookies | ||
* | ||
* The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies. | ||
* | ||
* | ||
* <div doc-module-components="ngCookies"></div> | ||
* | ||
* See {@link ngCookies.$cookies `$cookies`} for usage. | ||
*/ | ||
angular.module('ngCookies', ['ng']). | ||
/** | ||
* @ngdoc provider | ||
* @name $cookiesProvider | ||
* @description | ||
* Use `$cookiesProvider` to change the default behavior of the {@link ngCookies.$cookies $cookies} service. | ||
* */ | ||
provider('$cookies', [function $CookiesProvider() { | ||
/** | ||
* @ngdoc module | ||
* @name ngCookies | ||
* @ngdoc property | ||
* @name $cookiesProvider#defaults | ||
* @description | ||
* | ||
* # ngCookies | ||
* Object containing default options to pass when setting cookies. | ||
* | ||
* The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies. | ||
* The object may have following properties: | ||
* | ||
* - **path** - `{string}` - The cookie will be available only for this path and its | ||
* sub-paths. By default, this would be the URL that appears in your base tag. | ||
* - **domain** - `{string}` - The cookie will be available only for this domain and | ||
* its sub-domains. For obvious security reasons the user agent will not accept the | ||
* cookie if the current domain is not a sub domain or equals to the requested domain. | ||
* - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT" | ||
* or a Date object indicating the exact date/time this cookie will expire. | ||
* - **secure** - `{boolean}` - The cookie will be available only in secured connection. | ||
* | ||
* <div doc-module-components="ngCookies"></div> | ||
* Note: by default the address that appears in your `<base>` tag will be used as path. | ||
* This is important so that cookies will be visible for all routes in case html5mode is enabled | ||
* | ||
* See {@link ngCookies.$cookies `$cookies`} and | ||
* {@link ngCookies.$cookieStore `$cookieStore`} for usage. | ||
*/ | ||
**/ | ||
var defaults = this.defaults = {}; | ||
function calcOptions(options) { | ||
return options ? angular.extend({}, defaults, options) : defaults; | ||
} | ||
angular.module('ngCookies', ['ng']). | ||
/** | ||
@@ -34,5 +67,7 @@ * @ngdoc service | ||
* | ||
* Only a simple Object is exposed and by adding or removing properties to/from this object, new | ||
* cookies are created/deleted at the end of current $eval. | ||
* The object's properties can only be strings. | ||
* <div class="alert alert-info"> | ||
* Up until Angular 1.3, `$cookies` exposed properties that represented the | ||
* current browser cookie values. In version 1.4, this behavior has changed, and | ||
* `$cookies` now provides a standard api of getters, setters etc. | ||
* </div> | ||
* | ||
@@ -46,164 +81,243 @@ * Requires the {@link ngCookies `ngCookies`} module to be installed. | ||
* .controller('ExampleController', ['$cookies', function($cookies) { | ||
* // Retrieving a cookie | ||
* var favoriteCookie = $cookies.myFavorite; | ||
* // Setting a cookie | ||
* $cookies.myFavorite = 'oatmeal'; | ||
* }]); | ||
* // Retrieving a cookie | ||
* var favoriteCookie = $cookies.get('myFavorite'); | ||
* // Setting a cookie | ||
* $cookies.put('myFavorite', 'oatmeal'); | ||
* }]); | ||
* ``` | ||
*/ | ||
factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) { | ||
var cookies = {}, | ||
lastCookies = {}, | ||
lastBrowserCookies, | ||
runEval = false, | ||
copy = angular.copy, | ||
isUndefined = angular.isUndefined; | ||
this.$get = ['$$cookieReader', '$$cookieWriter', function($$cookieReader, $$cookieWriter) { | ||
return { | ||
/** | ||
* @ngdoc method | ||
* @name $cookies#get | ||
* | ||
* @description | ||
* Returns the value of given cookie key | ||
* | ||
* @param {string} key Id to use for lookup. | ||
* @returns {string} Raw cookie value. | ||
*/ | ||
get: function(key) { | ||
return $$cookieReader()[key]; | ||
}, | ||
//creates a poller fn that copies all cookies from the $browser to service & inits the service | ||
$browser.addPollFn(function () { | ||
var currentCookies = $browser.cookies(); | ||
if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl | ||
lastBrowserCookies = currentCookies; | ||
copy(currentCookies, lastCookies); | ||
copy(currentCookies, cookies); | ||
if (runEval) $rootScope.$apply(); | ||
} | ||
})(); | ||
/** | ||
* @ngdoc method | ||
* @name $cookies#getObject | ||
* | ||
* @description | ||
* Returns the deserialized value of given cookie key | ||
* | ||
* @param {string} key Id to use for lookup. | ||
* @returns {Object} Deserialized cookie value. | ||
*/ | ||
getObject: function(key) { | ||
var value = this.get(key); | ||
return value ? angular.fromJson(value) : value; | ||
}, | ||
runEval = true; | ||
/** | ||
* @ngdoc method | ||
* @name $cookies#getAll | ||
* | ||
* @description | ||
* Returns a key value object with all the cookies | ||
* | ||
* @returns {Object} All cookies | ||
*/ | ||
getAll: function() { | ||
return $$cookieReader(); | ||
}, | ||
//at the end of each eval, push cookies | ||
//TODO: this should happen before the "delayed" watches fire, because if some cookies are not | ||
// strings or browser refuses to store some cookies, we update the model in the push fn. | ||
$rootScope.$watch(push); | ||
/** | ||
* @ngdoc method | ||
* @name $cookies#put | ||
* | ||
* @description | ||
* Sets a value for given cookie key | ||
* | ||
* @param {string} key Id for the `value`. | ||
* @param {string} value Raw value to be stored. | ||
* @param {Object=} options Options object. | ||
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} | ||
*/ | ||
put: function(key, value, options) { | ||
$$cookieWriter(key, value, calcOptions(options)); | ||
}, | ||
return cookies; | ||
/** | ||
* @ngdoc method | ||
* @name $cookies#putObject | ||
* | ||
* @description | ||
* Serializes and sets a value for given cookie key | ||
* | ||
* @param {string} key Id for the `value`. | ||
* @param {Object} value Value to be stored. | ||
* @param {Object=} options Options object. | ||
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} | ||
*/ | ||
putObject: function(key, value, options) { | ||
this.put(key, angular.toJson(value), options); | ||
}, | ||
/** | ||
* @ngdoc method | ||
* @name $cookies#remove | ||
* | ||
* @description | ||
* Remove given cookie | ||
* | ||
* @param {string} key Id of the key-value pair to delete. | ||
* @param {Object=} options Options object. | ||
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} | ||
*/ | ||
remove: function(key, options) { | ||
$$cookieWriter(key, undefined, calcOptions(options)); | ||
} | ||
}; | ||
}]; | ||
}]); | ||
/** | ||
* Pushes all the cookies from the service to the browser and verifies if all cookies were | ||
* stored. | ||
*/ | ||
function push() { | ||
var name, | ||
value, | ||
browserCookies, | ||
updated; | ||
angular.module('ngCookies'). | ||
/** | ||
* @ngdoc service | ||
* @name $cookieStore | ||
* @deprecated | ||
* @requires $cookies | ||
* | ||
* @description | ||
* Provides a key-value (string-object) storage, that is backed by session cookies. | ||
* Objects put or retrieved from this storage are automatically serialized or | ||
* deserialized by angular's toJson/fromJson. | ||
* | ||
* Requires the {@link ngCookies `ngCookies`} module to be installed. | ||
* | ||
* <div class="alert alert-danger"> | ||
* **Note:** The $cookieStore service is **deprecated**. | ||
* Please use the {@link ngCookies.$cookies `$cookies`} service instead. | ||
* </div> | ||
* | ||
* @example | ||
* | ||
* ```js | ||
* angular.module('cookieStoreExample', ['ngCookies']) | ||
* .controller('ExampleController', ['$cookieStore', function($cookieStore) { | ||
* // Put cookie | ||
* $cookieStore.put('myFavorite','oatmeal'); | ||
* // Get cookie | ||
* var favoriteCookie = $cookieStore.get('myFavorite'); | ||
* // Removing a cookie | ||
* $cookieStore.remove('myFavorite'); | ||
* }]); | ||
* ``` | ||
*/ | ||
factory('$cookieStore', ['$cookies', function($cookies) { | ||
//delete any cookies deleted in $cookies | ||
for (name in lastCookies) { | ||
if (isUndefined(cookies[name])) { | ||
$browser.cookies(name, undefined); | ||
} | ||
} | ||
return { | ||
/** | ||
* @ngdoc method | ||
* @name $cookieStore#get | ||
* | ||
* @description | ||
* Returns the value of given cookie key | ||
* | ||
* @param {string} key Id to use for lookup. | ||
* @returns {Object} Deserialized cookie value, undefined if the cookie does not exist. | ||
*/ | ||
get: function(key) { | ||
return $cookies.getObject(key); | ||
}, | ||
//update all cookies updated in $cookies | ||
for (name in cookies) { | ||
value = cookies[name]; | ||
if (!angular.isString(value)) { | ||
value = '' + value; | ||
cookies[name] = value; | ||
} | ||
if (value !== lastCookies[name]) { | ||
$browser.cookies(name, value); | ||
updated = true; | ||
} | ||
} | ||
/** | ||
* @ngdoc method | ||
* @name $cookieStore#put | ||
* | ||
* @description | ||
* Sets a value for given cookie key | ||
* | ||
* @param {string} key Id for the `value`. | ||
* @param {Object} value Value to be stored. | ||
*/ | ||
put: function(key, value) { | ||
$cookies.putObject(key, value); | ||
}, | ||
//verify what was actually stored | ||
if (updated) { | ||
updated = false; | ||
browserCookies = $browser.cookies(); | ||
/** | ||
* @ngdoc method | ||
* @name $cookieStore#remove | ||
* | ||
* @description | ||
* Remove given cookie | ||
* | ||
* @param {string} key Id of the key-value pair to delete. | ||
*/ | ||
remove: function(key) { | ||
$cookies.remove(key); | ||
} | ||
}; | ||
for (name in cookies) { | ||
if (cookies[name] !== browserCookies[name]) { | ||
//delete or reset all cookies that the browser dropped from $cookies | ||
if (isUndefined(browserCookies[name])) { | ||
delete cookies[name]; | ||
} else { | ||
cookies[name] = browserCookies[name]; | ||
} | ||
updated = true; | ||
} | ||
} | ||
} | ||
} | ||
}]). | ||
}]); | ||
/** | ||
* @name $$cookieWriter | ||
* @requires $document | ||
* | ||
* @description | ||
* This is a private service for writing cookies | ||
* | ||
* @param {string} name Cookie name | ||
* @param {string=} value Cookie value (if undefined, cookie will be deleted) | ||
* @param {Object=} options Object with options that need to be stored for the cookie. | ||
*/ | ||
function $$CookieWriter($document, $log, $browser) { | ||
var cookiePath = $browser.baseHref(); | ||
var rawDocument = $document[0]; | ||
/** | ||
* @ngdoc service | ||
* @name $cookieStore | ||
* @requires $cookies | ||
* | ||
* @description | ||
* Provides a key-value (string-object) storage, that is backed by session cookies. | ||
* Objects put or retrieved from this storage are automatically serialized or | ||
* deserialized by angular's toJson/fromJson. | ||
* | ||
* Requires the {@link ngCookies `ngCookies`} module to be installed. | ||
* | ||
* @example | ||
* | ||
* ```js | ||
* angular.module('cookieStoreExample', ['ngCookies']) | ||
* .controller('ExampleController', ['$cookieStore', function($cookieStore) { | ||
* // Put cookie | ||
* $cookieStore.put('myFavorite','oatmeal'); | ||
* // Get cookie | ||
* var favoriteCookie = $cookieStore.get('myFavorite'); | ||
* // Removing a cookie | ||
* $cookieStore.remove('myFavorite'); | ||
* }]); | ||
* ``` | ||
*/ | ||
factory('$cookieStore', ['$cookies', function ($cookies) { | ||
function buildCookieString(name, value, options) { | ||
var path, expires; | ||
options = options || {}; | ||
expires = options.expires; | ||
path = angular.isDefined(options.path) ? options.path : cookiePath; | ||
if (angular.isUndefined(value)) { | ||
expires = 'Thu, 01 Jan 1970 00:00:00 GMT'; | ||
value = ''; | ||
} | ||
if (angular.isString(expires)) { | ||
expires = new Date(expires); | ||
} | ||
return { | ||
/** | ||
* @ngdoc method | ||
* @name $cookieStore#get | ||
* | ||
* @description | ||
* Returns the value of given cookie key | ||
* | ||
* @param {string} key Id to use for lookup. | ||
* @returns {Object} Deserialized cookie value. | ||
*/ | ||
get: function (key) { | ||
var value = $cookies[key]; | ||
return value ? angular.fromJson(value) : value; | ||
}, | ||
var str = encodeURIComponent(name) + '=' + encodeURIComponent(value); | ||
str += path ? ';path=' + path : ''; | ||
str += options.domain ? ';domain=' + options.domain : ''; | ||
str += expires ? ';expires=' + expires.toUTCString() : ''; | ||
str += options.secure ? ';secure' : ''; | ||
/** | ||
* @ngdoc method | ||
* @name $cookieStore#put | ||
* | ||
* @description | ||
* Sets a value for given cookie key | ||
* | ||
* @param {string} key Id for the `value`. | ||
* @param {Object} value Value to be stored. | ||
*/ | ||
put: function (key, value) { | ||
$cookies[key] = angular.toJson(value); | ||
}, | ||
// per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: | ||
// - 300 cookies | ||
// - 20 cookies per unique domain | ||
// - 4096 bytes per cookie | ||
var cookieLength = str.length + 1; | ||
if (cookieLength > 4096) { | ||
$log.warn("Cookie '" + name + | ||
"' possibly not set or overflowed because it was too large (" + | ||
cookieLength + " > 4096 bytes)!"); | ||
} | ||
/** | ||
* @ngdoc method | ||
* @name $cookieStore#remove | ||
* | ||
* @description | ||
* Remove given cookie | ||
* | ||
* @param {string} key Id of the key-value pair to delete. | ||
*/ | ||
remove: function (key) { | ||
delete $cookies[key]; | ||
} | ||
}; | ||
return str; | ||
} | ||
}]); | ||
return function(name, value, options) { | ||
rawDocument.cookie = buildCookieString(name, value, options); | ||
}; | ||
} | ||
$$CookieWriter.$inject = ['$document', '$log', '$browser']; | ||
angular.module('ngCookies').provider('$$cookieWriter', function $$CookieWriterProvider() { | ||
this.$get = $$CookieWriter; | ||
}); | ||
})(window, window.angular); |
/** | ||
* @license AngularJS v1.3.4 | ||
* (c) 2010-2014 Google, Inc. http://angularjs.org | ||
* @license AngularJS v1.4.8 | ||
* (c) 2010-2015 Google, Inc. http://angularjs.org | ||
* License: MIT | ||
*/ | ||
(function (window, angular, undefined) { | ||
'use strict'; | ||
(function(window, angular, undefined) {'use strict'; | ||
/** | ||
* @ngdoc module | ||
* @name ngRoute | ||
* @description | ||
* | ||
* # ngRoute | ||
* | ||
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps. | ||
* | ||
* ## Example | ||
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`. | ||
* | ||
* | ||
* <div doc-module-components="ngRoute"></div> | ||
*/ | ||
/* global -ngRouteModule */ | ||
var ngRouteModule = angular.module('ngRoute', ['ng']). | ||
provider('$route', $RouteProvider), | ||
$routeMinErr = angular.$$minErr('ngRoute'); | ||
/** | ||
* @ngdoc module | ||
* @name ngRoute | ||
* @description | ||
* | ||
* # ngRoute | ||
* | ||
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps. | ||
* | ||
* ## Example | ||
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`. | ||
* | ||
* | ||
* <div doc-module-components="ngRoute"></div> | ||
*/ | ||
/* global -ngRouteModule */ | ||
var ngRouteModule = angular.module('ngRoute', ['ng']). | ||
provider('$route', $RouteProvider), | ||
$routeMinErr = angular.$$minErr('ngRoute'); | ||
/** | ||
* @ngdoc provider | ||
* @name $routeProvider | ||
* | ||
* @description | ||
* | ||
* Used for configuring routes. | ||
* | ||
* ## Example | ||
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`. | ||
* | ||
* ## Dependencies | ||
* Requires the {@link ngRoute `ngRoute`} module to be installed. | ||
*/ | ||
function $RouteProvider() { | ||
function inherit(parent, extra) { | ||
return angular.extend(Object.create(parent), extra); | ||
} | ||
/** | ||
* @ngdoc provider | ||
* @name $routeProvider | ||
* | ||
* @description | ||
* | ||
* Used for configuring routes. | ||
* | ||
* ## Example | ||
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`. | ||
* | ||
* ## Dependencies | ||
* Requires the {@link ngRoute `ngRoute`} module to be installed. | ||
*/ | ||
function $RouteProvider() { | ||
function inherit(parent, extra) { | ||
return angular.extend(Object.create(parent), extra); | ||
} | ||
var routes = {}; | ||
var routes = {}; | ||
/** | ||
* @ngdoc method | ||
* @name $routeProvider#when | ||
* | ||
* @param {string} path Route path (matched against `$location.path`). If `$location.path` | ||
* contains redundant trailing slash or is missing one, the route will still match and the | ||
* `$location.path` will be updated to add or drop the trailing slash to exactly match the | ||
* route definition. | ||
* | ||
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up | ||
* to the next slash are matched and stored in `$routeParams` under the given `name` | ||
* when the route matches. | ||
* * `path` can contain named groups starting with a colon and ending with a star: | ||
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name` | ||
* when the route matches. | ||
* * `path` can contain optional named groups with a question mark: e.g.`:name?`. | ||
* | ||
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match | ||
* `/color/brown/largecode/code/with/slashes/edit` and extract: | ||
* | ||
* * `color: brown` | ||
* * `largecode: code/with/slashes`. | ||
* | ||
* | ||
* @param {Object} route Mapping information to be assigned to `$route.current` on route | ||
* match. | ||
* | ||
* Object properties: | ||
* | ||
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with | ||
* newly created scope or the name of a {@link angular.Module#controller registered | ||
/** | ||
* @ngdoc method | ||
* @name $routeProvider#when | ||
* | ||
* @param {string} path Route path (matched against `$location.path`). If `$location.path` | ||
* contains redundant trailing slash or is missing one, the route will still match and the | ||
* `$location.path` will be updated to add or drop the trailing slash to exactly match the | ||
* route definition. | ||
* | ||
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up | ||
* to the next slash are matched and stored in `$routeParams` under the given `name` | ||
* when the route matches. | ||
* * `path` can contain named groups starting with a colon and ending with a star: | ||
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name` | ||
* when the route matches. | ||
* * `path` can contain optional named groups with a question mark: e.g.`:name?`. | ||
* | ||
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match | ||
* `/color/brown/largecode/code/with/slashes/edit` and extract: | ||
* | ||
* * `color: brown` | ||
* * `largecode: code/with/slashes`. | ||
* | ||
* | ||
* @param {Object} route Mapping information to be assigned to `$route.current` on route | ||
* match. | ||
* | ||
* Object properties: | ||
* | ||
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with | ||
* newly created scope or the name of a {@link angular.Module#controller registered | ||
* controller} if passed as a string. | ||
* - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be | ||
* published to scope under the `controllerAs` name. | ||
* - `template` – `{string=|function()=}` – html template as a string or a function that | ||
* returns an html template as a string which should be used by {@link | ||
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives. | ||
* This property takes precedence over `templateUrl`. | ||
* | ||
* If `template` is a function, it will be called with the following parameters: | ||
* | ||
* - `{Array.<Object>}` - route parameters extracted from the current | ||
* `$location.path()` by applying the current route | ||
* | ||
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html | ||
* template that should be used by {@link ngRoute.directive:ngView ngView}. | ||
* | ||
* If `templateUrl` is a function, it will be called with the following parameters: | ||
* | ||
* - `{Array.<Object>}` - route parameters extracted from the current | ||
* `$location.path()` by applying the current route | ||
* | ||
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should | ||
* be injected into the controller. If any of these dependencies are promises, the router | ||
* will wait for them all to be resolved or one to be rejected before the controller is | ||
* instantiated. | ||
* If all the promises are resolved successfully, the values of the resolved promises are | ||
* injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is | ||
* fired. If any of the promises are rejected the | ||
* {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object | ||
* is: | ||
* | ||
* - `key` – `{string}`: a name of a dependency to be injected into the controller. | ||
* - `factory` - `{string|function}`: If `string` then it is an alias for a service. | ||
* Otherwise if function, then it is {@link auto.$injector#invoke injected} | ||
* and the return value is treated as the dependency. If the result is a promise, it is | ||
* resolved before its value is injected into the controller. Be aware that | ||
* `ngRoute.$routeParams` will still refer to the previous route within these resolve | ||
* functions. Use `$route.current.params` to access the new route parameters, instead. | ||
* | ||
* - `redirectTo` – {(string|function())=} – value to update | ||
* {@link ng.$location $location} path with and trigger route redirection. | ||
* | ||
* If `redirectTo` is a function, it will be called with the following parameters: | ||
* | ||
* - `{Object.<string>}` - route parameters extracted from the current | ||
* `$location.path()` by applying the current route templateUrl. | ||
* - `{string}` - current `$location.path()` | ||
* - `{Object}` - current `$location.search()` | ||
* | ||
* The custom `redirectTo` function is expected to return a string which will be used | ||
* to update `$location.path()` and `$location.search()`. | ||
* | ||
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()` | ||
* or `$location.hash()` changes. | ||
* | ||
* If the option is set to `false` and url in the browser changes, then | ||
* `$routeUpdate` event is broadcasted on the root scope. | ||
* | ||
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive | ||
* | ||
* If the option is set to `true`, then the particular route can be matched without being | ||
* case sensitive | ||
* | ||
* @returns {Object} self | ||
* | ||
* @description | ||
* Adds a new route definition to the `$route` service. | ||
*/ | ||
this.when = function (path, route) { | ||
//copy original route object to preserve params inherited from proto chain | ||
var routeCopy = angular.copy(route); | ||
if (angular.isUndefined(routeCopy.reloadOnSearch)) { | ||
routeCopy.reloadOnSearch = true; | ||
} | ||
if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) { | ||
routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch; | ||
} | ||
routes[path] = angular.extend( | ||
routeCopy, | ||
path && pathRegExp(path, routeCopy) | ||
); | ||
* - `controllerAs` – `{string=}` – An identifier name for a reference to the controller. | ||
* If present, the controller will be published to scope under the `controllerAs` name. | ||
* - `template` – `{string=|function()=}` – html template as a string or a function that | ||
* returns an html template as a string which should be used by {@link | ||
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives. | ||
* This property takes precedence over `templateUrl`. | ||
* | ||
* If `template` is a function, it will be called with the following parameters: | ||
* | ||
* - `{Array.<Object>}` - route parameters extracted from the current | ||
* `$location.path()` by applying the current route | ||
* | ||
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html | ||
* template that should be used by {@link ngRoute.directive:ngView ngView}. | ||
* | ||
* If `templateUrl` is a function, it will be called with the following parameters: | ||
* | ||
* - `{Array.<Object>}` - route parameters extracted from the current | ||
* `$location.path()` by applying the current route | ||
* | ||
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should | ||
* be injected into the controller. If any of these dependencies are promises, the router | ||
* will wait for them all to be resolved or one to be rejected before the controller is | ||
* instantiated. | ||
* If all the promises are resolved successfully, the values of the resolved promises are | ||
* injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is | ||
* fired. If any of the promises are rejected the | ||
* {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object | ||
* is: | ||
* | ||
* - `key` – `{string}`: a name of a dependency to be injected into the controller. | ||
* - `factory` - `{string|function}`: If `string` then it is an alias for a service. | ||
* Otherwise if function, then it is {@link auto.$injector#invoke injected} | ||
* and the return value is treated as the dependency. If the result is a promise, it is | ||
* resolved before its value is injected into the controller. Be aware that | ||
* `ngRoute.$routeParams` will still refer to the previous route within these resolve | ||
* functions. Use `$route.current.params` to access the new route parameters, instead. | ||
* | ||
* - `redirectTo` – {(string|function())=} – value to update | ||
* {@link ng.$location $location} path with and trigger route redirection. | ||
* | ||
* If `redirectTo` is a function, it will be called with the following parameters: | ||
* | ||
* - `{Object.<string>}` - route parameters extracted from the current | ||
* `$location.path()` by applying the current route templateUrl. | ||
* - `{string}` - current `$location.path()` | ||
* - `{Object}` - current `$location.search()` | ||
* | ||
* The custom `redirectTo` function is expected to return a string which will be used | ||
* to update `$location.path()` and `$location.search()`. | ||
* | ||
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()` | ||
* or `$location.hash()` changes. | ||
* | ||
* If the option is set to `false` and url in the browser changes, then | ||
* `$routeUpdate` event is broadcasted on the root scope. | ||
* | ||
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive | ||
* | ||
* If the option is set to `true`, then the particular route can be matched without being | ||
* case sensitive | ||
* | ||
* @returns {Object} self | ||
* | ||
* @description | ||
* Adds a new route definition to the `$route` service. | ||
*/ | ||
this.when = function(path, route) { | ||
//copy original route object to preserve params inherited from proto chain | ||
var routeCopy = angular.copy(route); | ||
if (angular.isUndefined(routeCopy.reloadOnSearch)) { | ||
routeCopy.reloadOnSearch = true; | ||
} | ||
if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) { | ||
routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch; | ||
} | ||
routes[path] = angular.extend( | ||
routeCopy, | ||
path && pathRegExp(path, routeCopy) | ||
); | ||
// create redirection for trailing slashes | ||
if (path) { | ||
var redirectPath = (path[path.length - 1] == '/') | ||
? path.substr(0, path.length - 1) | ||
: path + '/'; | ||
// create redirection for trailing slashes | ||
if (path) { | ||
var redirectPath = (path[path.length - 1] == '/') | ||
? path.substr(0, path.length - 1) | ||
: path + '/'; | ||
routes[redirectPath] = angular.extend( | ||
{redirectTo: path}, | ||
pathRegExp(redirectPath, routeCopy) | ||
); | ||
} | ||
routes[redirectPath] = angular.extend( | ||
{redirectTo: path}, | ||
pathRegExp(redirectPath, routeCopy) | ||
); | ||
} | ||
return this; | ||
}; | ||
return this; | ||
}; | ||
/** | ||
* @ngdoc property | ||
* @name $routeProvider#caseInsensitiveMatch | ||
* @description | ||
* | ||
* A boolean property indicating if routes defined | ||
* using this provider should be matched using a case sensitive | ||
* algorithm. Defaults to `false`. | ||
*/ | ||
this.caseInsensitiveMatch = false; | ||
/** | ||
* @ngdoc property | ||
* @name $routeProvider#caseInsensitiveMatch | ||
* @description | ||
* | ||
* A boolean property indicating if routes defined | ||
* using this provider should be matched using a case insensitive | ||
* algorithm. Defaults to `false`. | ||
*/ | ||
this.caseInsensitiveMatch = false; | ||
/** | ||
* @param path {string} path | ||
* @param opts {Object} options | ||
* @return {?Object} | ||
* | ||
* @description | ||
* Normalizes the given path, returning a regular expression | ||
* and the original path. | ||
* | ||
* Inspired by pathRexp in visionmedia/express/lib/utils.js. | ||
*/ | ||
function pathRegExp(path, opts) { | ||
var insensitive = opts.caseInsensitiveMatch, | ||
ret = { | ||
originalPath: path, | ||
regexp: path | ||
}, | ||
keys = ret.keys = []; | ||
/** | ||
* @param path {string} path | ||
* @param opts {Object} options | ||
* @return {?Object} | ||
* | ||
* @description | ||
* Normalizes the given path, returning a regular expression | ||
* and the original path. | ||
* | ||
* Inspired by pathRexp in visionmedia/express/lib/utils.js. | ||
*/ | ||
function pathRegExp(path, opts) { | ||
var insensitive = opts.caseInsensitiveMatch, | ||
ret = { | ||
originalPath: path, | ||
regexp: path | ||
}, | ||
keys = ret.keys = []; | ||
path = path | ||
.replace(/([().])/g, '\\$1') | ||
.replace(/(\/)?:(\w+)([\?\*])?/g, function (_, slash, key, option) { | ||
var optional = option === '?' ? option : null; | ||
var star = option === '*' ? option : null; | ||
keys.push({name: key, optional: !!optional}); | ||
slash = slash || ''; | ||
return '' | ||
+ (optional ? '' : slash) | ||
+ '(?:' | ||
+ (optional ? slash : '') | ||
+ (star && '(.+?)' || '([^/]+)') | ||
+ (optional || '') | ||
+ ')' | ||
+ (optional || ''); | ||
}) | ||
.replace(/([\/$\*])/g, '\\$1'); | ||
path = path | ||
.replace(/([().])/g, '\\$1') | ||
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) { | ||
var optional = option === '?' ? option : null; | ||
var star = option === '*' ? option : null; | ||
keys.push({ name: key, optional: !!optional }); | ||
slash = slash || ''; | ||
return '' | ||
+ (optional ? '' : slash) | ||
+ '(?:' | ||
+ (optional ? slash : '') | ||
+ (star && '(.+?)' || '([^/]+)') | ||
+ (optional || '') | ||
+ ')' | ||
+ (optional || ''); | ||
}) | ||
.replace(/([\/$\*])/g, '\\$1'); | ||
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : ''); | ||
return ret; | ||
} | ||
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : ''); | ||
return ret; | ||
} | ||
/** | ||
* @ngdoc method | ||
* @name $routeProvider#otherwise | ||
* | ||
* @description | ||
* Sets route definition that will be used on route change when no other route definition | ||
* is matched. | ||
* | ||
* @param {Object|string} params Mapping information to be assigned to `$route.current`. | ||
* If called with a string, the value maps to `redirectTo`. | ||
* @returns {Object} self | ||
*/ | ||
this.otherwise = function (params) { | ||
if (typeof params === 'string') { | ||
params = {redirectTo: params}; | ||
} | ||
this.when(null, params); | ||
return this; | ||
}; | ||
/** | ||
* @ngdoc method | ||
* @name $routeProvider#otherwise | ||
* | ||
* @description | ||
* Sets route definition that will be used on route change when no other route definition | ||
* is matched. | ||
* | ||
* @param {Object|string} params Mapping information to be assigned to `$route.current`. | ||
* If called with a string, the value maps to `redirectTo`. | ||
* @returns {Object} self | ||
*/ | ||
this.otherwise = function(params) { | ||
if (typeof params === 'string') { | ||
params = {redirectTo: params}; | ||
} | ||
this.when(null, params); | ||
return this; | ||
}; | ||
this.$get = ['$rootScope', | ||
'$location', | ||
'$routeParams', | ||
'$q', | ||
'$injector', | ||
'$templateRequest', | ||
'$sce', | ||
function ($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) { | ||
this.$get = ['$rootScope', | ||
'$location', | ||
'$routeParams', | ||
'$q', | ||
'$injector', | ||
'$templateRequest', | ||
'$sce', | ||
function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) { | ||
/** | ||
* @ngdoc service | ||
* @name $route | ||
* @requires $location | ||
* @requires $routeParams | ||
* | ||
* @property {Object} current Reference to the current route definition. | ||
* The route definition contains: | ||
* | ||
* - `controller`: The controller constructor as define in route definition. | ||
* - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for | ||
* controller instantiation. The `locals` contain | ||
* the resolved values of the `resolve` map. Additionally the `locals` also contain: | ||
* | ||
* - `$scope` - The current route scope. | ||
* - `$template` - The current route template HTML. | ||
* | ||
* @property {Object} routes Object with all route configuration Objects as its properties. | ||
* | ||
* @description | ||
* `$route` is used for deep-linking URLs to controllers and views (HTML partials). | ||
* It watches `$location.url()` and tries to map the path to an existing route definition. | ||
* | ||
* Requires the {@link ngRoute `ngRoute`} module to be installed. | ||
* | ||
* You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API. | ||
* | ||
* The `$route` service is typically used in conjunction with the | ||
* {@link ngRoute.directive:ngView `ngView`} directive and the | ||
* {@link ngRoute.$routeParams `$routeParams`} service. | ||
* | ||
* @example | ||
* This example shows how changing the URL hash causes the `$route` to match a route against the | ||
* URL, and the `ngView` pulls in the partial. | ||
* | ||
* <example name="$route-service" module="ngRouteExample" | ||
* deps="angular-route.js" fixBase="true"> | ||
* <file name="index.html"> | ||
* <div ng-controller="MainController"> | ||
* Choose: | ||
* <a href="Book/Moby">Moby</a> | | ||
* <a href="Book/Moby/ch/1">Moby: Ch1</a> | | ||
* <a href="Book/Gatsby">Gatsby</a> | | ||
* <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> | | ||
* <a href="Book/Scarlet">Scarlet Letter</a><br/> | ||
* | ||
* <div ng-view></div> | ||
* | ||
* <hr /> | ||
* | ||
* <pre>$location.path() = {{$location.path()}}</pre> | ||
* <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre> | ||
* <pre>$route.current.params = {{$route.current.params}}</pre> | ||
* <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre> | ||
* <pre>$routeParams = {{$routeParams}}</pre> | ||
* </div> | ||
* </file> | ||
* | ||
* <file name="book.html"> | ||
* controller: {{name}}<br /> | ||
* Book Id: {{params.bookId}}<br /> | ||
* </file> | ||
* | ||
* <file name="chapter.html"> | ||
* controller: {{name}}<br /> | ||
* Book Id: {{params.bookId}}<br /> | ||
* Chapter Id: {{params.chapterId}} | ||
* </file> | ||
* | ||
* <file name="script.js"> | ||
* angular.module('ngRouteExample', ['ngRoute']) | ||
* | ||
* .controller('MainController', function($scope, $route, $routeParams, $location) { | ||
/** | ||
* @ngdoc service | ||
* @name $route | ||
* @requires $location | ||
* @requires $routeParams | ||
* | ||
* @property {Object} current Reference to the current route definition. | ||
* The route definition contains: | ||
* | ||
* - `controller`: The controller constructor as define in route definition. | ||
* - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for | ||
* controller instantiation. The `locals` contain | ||
* the resolved values of the `resolve` map. Additionally the `locals` also contain: | ||
* | ||
* - `$scope` - The current route scope. | ||
* - `$template` - The current route template HTML. | ||
* | ||
* @property {Object} routes Object with all route configuration Objects as its properties. | ||
* | ||
* @description | ||
* `$route` is used for deep-linking URLs to controllers and views (HTML partials). | ||
* It watches `$location.url()` and tries to map the path to an existing route definition. | ||
* | ||
* Requires the {@link ngRoute `ngRoute`} module to be installed. | ||
* | ||
* You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API. | ||
* | ||
* The `$route` service is typically used in conjunction with the | ||
* {@link ngRoute.directive:ngView `ngView`} directive and the | ||
* {@link ngRoute.$routeParams `$routeParams`} service. | ||
* | ||
* @example | ||
* This example shows how changing the URL hash causes the `$route` to match a route against the | ||
* URL, and the `ngView` pulls in the partial. | ||
* | ||
* <example name="$route-service" module="ngRouteExample" | ||
* deps="angular-route.js" fixBase="true"> | ||
* <file name="index.html"> | ||
* <div ng-controller="MainController"> | ||
* Choose: | ||
* <a href="Book/Moby">Moby</a> | | ||
* <a href="Book/Moby/ch/1">Moby: Ch1</a> | | ||
* <a href="Book/Gatsby">Gatsby</a> | | ||
* <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> | | ||
* <a href="Book/Scarlet">Scarlet Letter</a><br/> | ||
* | ||
* <div ng-view></div> | ||
* | ||
* <hr /> | ||
* | ||
* <pre>$location.path() = {{$location.path()}}</pre> | ||
* <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre> | ||
* <pre>$route.current.params = {{$route.current.params}}</pre> | ||
* <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre> | ||
* <pre>$routeParams = {{$routeParams}}</pre> | ||
* </div> | ||
* </file> | ||
* | ||
* <file name="book.html"> | ||
* controller: {{name}}<br /> | ||
* Book Id: {{params.bookId}}<br /> | ||
* </file> | ||
* | ||
* <file name="chapter.html"> | ||
* controller: {{name}}<br /> | ||
* Book Id: {{params.bookId}}<br /> | ||
* Chapter Id: {{params.chapterId}} | ||
* </file> | ||
* | ||
* <file name="script.js"> | ||
* angular.module('ngRouteExample', ['ngRoute']) | ||
* | ||
* .controller('MainController', function($scope, $route, $routeParams, $location) { | ||
* $scope.$route = $route; | ||
@@ -337,14 +336,14 @@ * $scope.$location = $location; | ||
* }) | ||
* | ||
* .controller('BookController', function($scope, $routeParams) { | ||
* | ||
* .controller('BookController', function($scope, $routeParams) { | ||
* $scope.name = "BookController"; | ||
* $scope.params = $routeParams; | ||
* }) | ||
* | ||
* .controller('ChapterController', function($scope, $routeParams) { | ||
* | ||
* .controller('ChapterController', function($scope, $routeParams) { | ||
* $scope.name = "ChapterController"; | ||
* $scope.params = $routeParams; | ||
* }) | ||
* | ||
* .config(function($routeProvider, $locationProvider) { | ||
* | ||
* .config(function($routeProvider, $locationProvider) { | ||
* $routeProvider | ||
@@ -371,7 +370,7 @@ * .when('/Book/:bookId', { | ||
* }); | ||
* | ||
* </file> | ||
* | ||
* <file name="protractor.js" type="protractor"> | ||
* it('should load and compile correct template', function() { | ||
* | ||
* </file> | ||
* | ||
* <file name="protractor.js" type="protractor"> | ||
* it('should load and compile correct template', function() { | ||
* element(by.linkText('Moby: Ch1')).click(); | ||
@@ -389,411 +388,404 @@ * var content = element(by.css('[ng-view]')).getText(); | ||
* }); | ||
* </file> | ||
* </example> | ||
*/ | ||
* </file> | ||
* </example> | ||
*/ | ||
/** | ||
* @ngdoc event | ||
* @name $route#$routeChangeStart | ||
* @eventType broadcast on root scope | ||
* @description | ||
* Broadcasted before a route change. At this point the route services starts | ||
* resolving all of the dependencies needed for the route change to occur. | ||
* Typically this involves fetching the view template as well as any dependencies | ||
* defined in `resolve` route property. Once all of the dependencies are resolved | ||
* `$routeChangeSuccess` is fired. | ||
* | ||
* The route change (and the `$location` change that triggered it) can be prevented | ||
* by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} | ||
* for more details about event object. | ||
* | ||
* @param {Object} angularEvent Synthetic event object. | ||
* @param {Route} next Future route information. | ||
* @param {Route} current Current route information. | ||
*/ | ||
/** | ||
* @ngdoc event | ||
* @name $route#$routeChangeStart | ||
* @eventType broadcast on root scope | ||
* @description | ||
* Broadcasted before a route change. At this point the route services starts | ||
* resolving all of the dependencies needed for the route change to occur. | ||
* Typically this involves fetching the view template as well as any dependencies | ||
* defined in `resolve` route property. Once all of the dependencies are resolved | ||
* `$routeChangeSuccess` is fired. | ||
* | ||
* The route change (and the `$location` change that triggered it) can be prevented | ||
* by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} | ||
* for more details about event object. | ||
* | ||
* @param {Object} angularEvent Synthetic event object. | ||
* @param {Route} next Future route information. | ||
* @param {Route} current Current route information. | ||
*/ | ||
/** | ||
* @ngdoc event | ||
* @name $route#$routeChangeSuccess | ||
* @eventType broadcast on root scope | ||
* @description | ||
* Broadcasted after a route dependencies are resolved. | ||
* {@link ngRoute.directive:ngView ngView} listens for the directive | ||
* to instantiate the controller and render the view. | ||
* | ||
* @param {Object} angularEvent Synthetic event object. | ||
* @param {Route} current Current route information. | ||
* @param {Route|Undefined} previous Previous route information, or undefined if current is | ||
* first route entered. | ||
*/ | ||
/** | ||
* @ngdoc event | ||
* @name $route#$routeChangeSuccess | ||
* @eventType broadcast on root scope | ||
* @description | ||
* Broadcasted after a route change has happened successfully. | ||
* The `resolve` dependencies are now available in the `current.locals` property. | ||
* | ||
* {@link ngRoute.directive:ngView ngView} listens for the directive | ||
* to instantiate the controller and render the view. | ||
* | ||
* @param {Object} angularEvent Synthetic event object. | ||
* @param {Route} current Current route information. | ||
* @param {Route|Undefined} previous Previous route information, or undefined if current is | ||
* first route entered. | ||
*/ | ||
/** | ||
* @ngdoc event | ||
* @name $route#$routeChangeError | ||
* @eventType broadcast on root scope | ||
* @description | ||
* Broadcasted if any of the resolve promises are rejected. | ||
* | ||
* @param {Object} angularEvent Synthetic event object | ||
* @param {Route} current Current route information. | ||
* @param {Route} previous Previous route information. | ||
* @param {Route} rejection Rejection of the promise. Usually the error of the failed promise. | ||
*/ | ||
/** | ||
* @ngdoc event | ||
* @name $route#$routeChangeError | ||
* @eventType broadcast on root scope | ||
* @description | ||
* Broadcasted if any of the resolve promises are rejected. | ||
* | ||
* @param {Object} angularEvent Synthetic event object | ||
* @param {Route} current Current route information. | ||
* @param {Route} previous Previous route information. | ||
* @param {Route} rejection Rejection of the promise. Usually the error of the failed promise. | ||
*/ | ||
/** | ||
* @ngdoc event | ||
* @name $route#$routeUpdate | ||
* @eventType broadcast on root scope | ||
* @description | ||
* | ||
* The `reloadOnSearch` property has been set to false, and we are reusing the same | ||
* instance of the Controller. | ||
*/ | ||
/** | ||
* @ngdoc event | ||
* @name $route#$routeUpdate | ||
* @eventType broadcast on root scope | ||
* @description | ||
* The `reloadOnSearch` property has been set to false, and we are reusing the same | ||
* instance of the Controller. | ||
* | ||
* @param {Object} angularEvent Synthetic event object | ||
* @param {Route} current Current/previous route information. | ||
*/ | ||
var forceReload = false, | ||
preparedRoute, | ||
preparedRouteIsUpdateOnly, | ||
$route = { | ||
routes: routes, | ||
var forceReload = false, | ||
preparedRoute, | ||
preparedRouteIsUpdateOnly, | ||
$route = { | ||
routes: routes, | ||
/** | ||
* @ngdoc method | ||
* @name $route#reload | ||
* | ||
* @description | ||
* Causes `$route` service to reload the current route even if | ||
* {@link ng.$location $location} hasn't changed. | ||
* | ||
* As a result of that, {@link ngRoute.directive:ngView ngView} | ||
* creates new scope and reinstantiates the controller. | ||
*/ | ||
reload: function () { | ||
forceReload = true; | ||
$rootScope.$evalAsync(function () { | ||
// Don't support cancellation of a reload for now... | ||
prepareRoute(); | ||
commitRoute(); | ||
}); | ||
}, | ||
/** | ||
* @ngdoc method | ||
* @name $route#reload | ||
* | ||
* @description | ||
* Causes `$route` service to reload the current route even if | ||
* {@link ng.$location $location} hasn't changed. | ||
* | ||
* As a result of that, {@link ngRoute.directive:ngView ngView} | ||
* creates new scope and reinstantiates the controller. | ||
*/ | ||
reload: function() { | ||
forceReload = true; | ||
$rootScope.$evalAsync(function() { | ||
// Don't support cancellation of a reload for now... | ||
prepareRoute(); | ||
commitRoute(); | ||
}); | ||
}, | ||
/** | ||
* @ngdoc method | ||
* @name $route#updateParams | ||
* | ||
* @description | ||
* Causes `$route` service to update the current URL, replacing | ||
* current route parameters with those specified in `newParams`. | ||
* Provided property names that match the route's path segment | ||
* definitions will be interpolated into the location's path, while | ||
* remaining properties will be treated as query params. | ||
* | ||
* @param {Object} newParams mapping of URL parameter names to values | ||
*/ | ||
updateParams: function (newParams) { | ||
if (this.current && this.current.$$route) { | ||
var searchParams = {}, self = this; | ||
/** | ||
* @ngdoc method | ||
* @name $route#updateParams | ||
* | ||
* @description | ||
* Causes `$route` service to update the current URL, replacing | ||
* current route parameters with those specified in `newParams`. | ||
* Provided property names that match the route's path segment | ||
* definitions will be interpolated into the location's path, while | ||
* remaining properties will be treated as query params. | ||
* | ||
* @param {!Object<string, string>} newParams mapping of URL parameter names to values | ||
*/ | ||
updateParams: function(newParams) { | ||
if (this.current && this.current.$$route) { | ||
newParams = angular.extend({}, this.current.params, newParams); | ||
$location.path(interpolate(this.current.$$route.originalPath, newParams)); | ||
// interpolate modifies newParams, only query params are left | ||
$location.search(newParams); | ||
} else { | ||
throw $routeMinErr('norout', 'Tried updating route when with no current route'); | ||
} | ||
} | ||
}; | ||
angular.forEach(Object.keys(newParams), function (key) { | ||
if (!self.current.pathParams[key]) searchParams[key] = newParams[key]; | ||
}); | ||
$rootScope.$on('$locationChangeStart', prepareRoute); | ||
$rootScope.$on('$locationChangeSuccess', commitRoute); | ||
newParams = angular.extend({}, this.current.params, newParams); | ||
$location.path(interpolate(this.current.$$route.originalPath, newParams)); | ||
$location.search(angular.extend({}, $location.search(), searchParams)); | ||
} | ||
else { | ||
throw $routeMinErr('norout', 'Tried updating route when with no current route'); | ||
} | ||
} | ||
}; | ||
return $route; | ||
$rootScope.$on('$locationChangeStart', prepareRoute); | ||
$rootScope.$on('$locationChangeSuccess', commitRoute); | ||
///////////////////////////////////////////////////// | ||
return $route; | ||
/** | ||
* @param on {string} current url | ||
* @param route {Object} route regexp to match the url against | ||
* @return {?Object} | ||
* | ||
* @description | ||
* Check if the route matches the current url. | ||
* | ||
* Inspired by match in | ||
* visionmedia/express/lib/router/router.js. | ||
*/ | ||
function switchRouteMatcher(on, route) { | ||
var keys = route.keys, | ||
params = {}; | ||
///////////////////////////////////////////////////// | ||
if (!route.regexp) return null; | ||
/** | ||
* @param on {string} current url | ||
* @param route {Object} route regexp to match the url against | ||
* @return {?Object} | ||
* | ||
* @description | ||
* Check if the route matches the current url. | ||
* | ||
* Inspired by match in | ||
* visionmedia/express/lib/router/router.js. | ||
*/ | ||
function switchRouteMatcher(on, route) { | ||
var keys = route.keys, | ||
params = {}; | ||
var m = route.regexp.exec(on); | ||
if (!m) return null; | ||
if (!route.regexp) return null; | ||
for (var i = 1, len = m.length; i < len; ++i) { | ||
var key = keys[i - 1]; | ||
var m = route.regexp.exec(on); | ||
if (!m) return null; | ||
var val = m[i]; | ||
for (var i = 1, len = m.length; i < len; ++i) { | ||
var key = keys[i - 1]; | ||
if (key && val) { | ||
params[key.name] = val; | ||
} | ||
} | ||
return params; | ||
} | ||
var val = m[i]; | ||
function prepareRoute($locationEvent) { | ||
var lastRoute = $route.current; | ||
if (key && val) { | ||
params[key.name] = val; | ||
} | ||
} | ||
return params; | ||
} | ||
preparedRoute = parseRoute(); | ||
preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route | ||
&& angular.equals(preparedRoute.pathParams, lastRoute.pathParams) | ||
&& !preparedRoute.reloadOnSearch && !forceReload; | ||
function prepareRoute($locationEvent) { | ||
var lastRoute = $route.current; | ||
if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) { | ||
if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) { | ||
if ($locationEvent) { | ||
$locationEvent.preventDefault(); | ||
} | ||
} | ||
} | ||
} | ||
preparedRoute = parseRoute(); | ||
preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route | ||
&& angular.equals(preparedRoute.pathParams, lastRoute.pathParams) | ||
&& !preparedRoute.reloadOnSearch && !forceReload; | ||
function commitRoute() { | ||
var lastRoute = $route.current; | ||
var nextRoute = preparedRoute; | ||
if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) { | ||
if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) { | ||
if ($locationEvent) { | ||
$locationEvent.preventDefault(); | ||
} | ||
} | ||
} | ||
} | ||
if (preparedRouteIsUpdateOnly) { | ||
lastRoute.params = nextRoute.params; | ||
angular.copy(lastRoute.params, $routeParams); | ||
$rootScope.$broadcast('$routeUpdate', lastRoute); | ||
} else if (nextRoute || lastRoute) { | ||
forceReload = false; | ||
$route.current = nextRoute; | ||
if (nextRoute) { | ||
if (nextRoute.redirectTo) { | ||
if (angular.isString(nextRoute.redirectTo)) { | ||
$location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params) | ||
.replace(); | ||
} else { | ||
$location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search())) | ||
.replace(); | ||
} | ||
} | ||
} | ||
function commitRoute() { | ||
var lastRoute = $route.current; | ||
var nextRoute = preparedRoute; | ||
$q.when(nextRoute). | ||
then(function() { | ||
if (nextRoute) { | ||
var locals = angular.extend({}, nextRoute.resolve), | ||
template, templateUrl; | ||
if (preparedRouteIsUpdateOnly) { | ||
lastRoute.params = nextRoute.params; | ||
angular.copy(lastRoute.params, $routeParams); | ||
$rootScope.$broadcast('$routeUpdate', lastRoute); | ||
} else if (nextRoute || lastRoute) { | ||
forceReload = false; | ||
$route.current = nextRoute; | ||
if (nextRoute) { | ||
if (nextRoute.redirectTo) { | ||
if (angular.isString(nextRoute.redirectTo)) { | ||
$location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params) | ||
.replace(); | ||
} else { | ||
$location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search())) | ||
.replace(); | ||
} | ||
} | ||
} | ||
angular.forEach(locals, function(value, key) { | ||
locals[key] = angular.isString(value) ? | ||
$injector.get(value) : $injector.invoke(value, null, null, key); | ||
}); | ||
$q.when(nextRoute). | ||
then(function () { | ||
if (nextRoute) { | ||
var locals = angular.extend({}, nextRoute.resolve), | ||
template, templateUrl; | ||
angular.forEach(locals, function (value, key) { | ||
locals[key] = angular.isString(value) ? | ||
$injector.get(value) : $injector.invoke(value, null, null, key); | ||
}); | ||
if (angular.isDefined(template = nextRoute.template)) { | ||
if (angular.isFunction(template)) { | ||
template = template(nextRoute.params); | ||
} | ||
} else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) { | ||
if (angular.isFunction(templateUrl)) { | ||
templateUrl = templateUrl(nextRoute.params); | ||
} | ||
templateUrl = $sce.getTrustedResourceUrl(templateUrl); | ||
if (angular.isDefined(templateUrl)) { | ||
nextRoute.loadedTemplateUrl = templateUrl; | ||
template = $templateRequest(templateUrl); | ||
} | ||
} | ||
if (angular.isDefined(template)) { | ||
locals['$template'] = template; | ||
} | ||
return $q.all(locals); | ||
} | ||
}). | ||
// after route change | ||
then(function (locals) { | ||
if (nextRoute == $route.current) { | ||
if (nextRoute) { | ||
nextRoute.locals = locals; | ||
angular.copy(nextRoute.params, $routeParams); | ||
} | ||
$rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute); | ||
} | ||
}, function (error) { | ||
if (nextRoute == $route.current) { | ||
$rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error); | ||
} | ||
}); | ||
} | ||
if (angular.isDefined(template = nextRoute.template)) { | ||
if (angular.isFunction(template)) { | ||
template = template(nextRoute.params); | ||
} | ||
/** | ||
* @returns {Object} the current active route, by matching it against the URL | ||
*/ | ||
function parseRoute() { | ||
// Match a route | ||
var params, match; | ||
angular.forEach(routes, function (route, path) { | ||
if (!match && (params = switchRouteMatcher($location.path(), route))) { | ||
match = inherit(route, { | ||
params: angular.extend({}, $location.search(), params), | ||
pathParams: params | ||
}); | ||
match.$$route = route; | ||
} | ||
}); | ||
// No route matched; fallback to "otherwise" route | ||
return match || routes[null] && inherit(routes[null], {params: {}, pathParams: {}}); | ||
} else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) { | ||
if (angular.isFunction(templateUrl)) { | ||
templateUrl = templateUrl(nextRoute.params); | ||
} | ||
/** | ||
* @returns {string} interpolation of the redirect path with the parameters | ||
*/ | ||
function interpolate(string, params) { | ||
var result = []; | ||
angular.forEach((string || '').split(':'), function (segment, i) { | ||
if (i === 0) { | ||
result.push(segment); | ||
} else { | ||
var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/); | ||
var key = segmentMatch[1]; | ||
result.push(params[key]); | ||
result.push(segmentMatch[2] || ''); | ||
delete params[key]; | ||
} | ||
}); | ||
return result.join(''); | ||
if (angular.isDefined(templateUrl)) { | ||
nextRoute.loadedTemplateUrl = $sce.valueOf(templateUrl); | ||
template = $templateRequest(templateUrl); | ||
} | ||
}]; | ||
} | ||
if (angular.isDefined(template)) { | ||
locals['$template'] = template; | ||
} | ||
return $q.all(locals); | ||
} | ||
}). | ||
then(function(locals) { | ||
// after route change | ||
if (nextRoute == $route.current) { | ||
if (nextRoute) { | ||
nextRoute.locals = locals; | ||
angular.copy(nextRoute.params, $routeParams); | ||
} | ||
$rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute); | ||
} | ||
}, function(error) { | ||
if (nextRoute == $route.current) { | ||
$rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error); | ||
} | ||
}); | ||
} | ||
} | ||
ngRouteModule.provider('$routeParams', $RouteParamsProvider); | ||
/** | ||
* @returns {Object} the current active route, by matching it against the URL | ||
*/ | ||
function parseRoute() { | ||
// Match a route | ||
var params, match; | ||
angular.forEach(routes, function(route, path) { | ||
if (!match && (params = switchRouteMatcher($location.path(), route))) { | ||
match = inherit(route, { | ||
params: angular.extend({}, $location.search(), params), | ||
pathParams: params}); | ||
match.$$route = route; | ||
} | ||
}); | ||
// No route matched; fallback to "otherwise" route | ||
return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}}); | ||
} | ||
/** | ||
* @ngdoc service | ||
* @name $routeParams | ||
* @requires $route | ||
* | ||
* @description | ||
* The `$routeParams` service allows you to retrieve the current set of route parameters. | ||
* | ||
* Requires the {@link ngRoute `ngRoute`} module to be installed. | ||
* | ||
* The route parameters are a combination of {@link ng.$location `$location`}'s | ||
* {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}. | ||
* The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched. | ||
* | ||
* In case of parameter name collision, `path` params take precedence over `search` params. | ||
* | ||
* The service guarantees that the identity of the `$routeParams` object will remain unchanged | ||
* (but its properties will likely change) even when a route change occurs. | ||
* | ||
* Note that the `$routeParams` are only updated *after* a route change completes successfully. | ||
* This means that you cannot rely on `$routeParams` being correct in route resolve functions. | ||
* Instead you can use `$route.current.params` to access the new route's parameters. | ||
* | ||
* @example | ||
* ```js | ||
* // Given: | ||
* // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby | ||
* // Route: /Chapter/:chapterId/Section/:sectionId | ||
* // | ||
* // Then | ||
* $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'} | ||
* ``` | ||
* @returns {string} interpolation of the redirect path with the parameters | ||
*/ | ||
function $RouteParamsProvider() { | ||
this.$get = function () { | ||
return {}; | ||
}; | ||
function interpolate(string, params) { | ||
var result = []; | ||
angular.forEach((string || '').split(':'), function(segment, i) { | ||
if (i === 0) { | ||
result.push(segment); | ||
} else { | ||
var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/); | ||
var key = segmentMatch[1]; | ||
result.push(params[key]); | ||
result.push(segmentMatch[2] || ''); | ||
delete params[key]; | ||
} | ||
}); | ||
return result.join(''); | ||
} | ||
}]; | ||
} | ||
ngRouteModule.directive('ngView', ngViewFactory); | ||
ngRouteModule.directive('ngView', ngViewFillContentFactory); | ||
ngRouteModule.provider('$routeParams', $RouteParamsProvider); | ||
/** | ||
* @ngdoc directive | ||
* @name ngView | ||
* @restrict ECA | ||
* | ||
* @description | ||
* # Overview | ||
* `ngView` is a directive that complements the {@link ngRoute.$route $route} service by | ||
* including the rendered template of the current route into the main layout (`index.html`) file. | ||
* Every time the current route changes, the included view changes with it according to the | ||
* configuration of the `$route` service. | ||
* | ||
* Requires the {@link ngRoute `ngRoute`} module to be installed. | ||
* | ||
* @animations | ||
* enter - animation is used to bring new content into the browser. | ||
* leave - animation is used to animate existing content away. | ||
* | ||
* The enter and leave animation occur concurrently. | ||
* | ||
* @scope | ||
* @priority 400 | ||
* @param {string=} onload Expression to evaluate whenever the view updates. | ||
* | ||
* @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll | ||
/** | ||
* @ngdoc service | ||
* @name $routeParams | ||
* @requires $route | ||
* | ||
* @description | ||
* The `$routeParams` service allows you to retrieve the current set of route parameters. | ||
* | ||
* Requires the {@link ngRoute `ngRoute`} module to be installed. | ||
* | ||
* The route parameters are a combination of {@link ng.$location `$location`}'s | ||
* {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}. | ||
* The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched. | ||
* | ||
* In case of parameter name collision, `path` params take precedence over `search` params. | ||
* | ||
* The service guarantees that the identity of the `$routeParams` object will remain unchanged | ||
* (but its properties will likely change) even when a route change occurs. | ||
* | ||
* Note that the `$routeParams` are only updated *after* a route change completes successfully. | ||
* This means that you cannot rely on `$routeParams` being correct in route resolve functions. | ||
* Instead you can use `$route.current.params` to access the new route's parameters. | ||
* | ||
* @example | ||
* ```js | ||
* // Given: | ||
* // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby | ||
* // Route: /Chapter/:chapterId/Section/:sectionId | ||
* // | ||
* // Then | ||
* $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'} | ||
* ``` | ||
*/ | ||
function $RouteParamsProvider() { | ||
this.$get = function() { return {}; }; | ||
} | ||
ngRouteModule.directive('ngView', ngViewFactory); | ||
ngRouteModule.directive('ngView', ngViewFillContentFactory); | ||
/** | ||
* @ngdoc directive | ||
* @name ngView | ||
* @restrict ECA | ||
* | ||
* @description | ||
* # Overview | ||
* `ngView` is a directive that complements the {@link ngRoute.$route $route} service by | ||
* including the rendered template of the current route into the main layout (`index.html`) file. | ||
* Every time the current route changes, the included view changes with it according to the | ||
* configuration of the `$route` service. | ||
* | ||
* Requires the {@link ngRoute `ngRoute`} module to be installed. | ||
* | ||
* @animations | ||
* enter - animation is used to bring new content into the browser. | ||
* leave - animation is used to animate existing content away. | ||
* | ||
* The enter and leave animation occur concurrently. | ||
* | ||
* @scope | ||
* @priority 400 | ||
* @param {string=} onload Expression to evaluate whenever the view updates. | ||
* | ||
* @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll | ||
* $anchorScroll} to scroll the viewport after the view is updated. | ||
* | ||
* - If the attribute is not set, disable scrolling. | ||
* - If the attribute is set without value, enable scrolling. | ||
* - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated | ||
* as an expression yields a truthy value. | ||
* @example | ||
<example name="ngView-directive" module="ngViewExample" | ||
deps="angular-route.js;angular-animate.js" | ||
animations="true" fixBase="true"> | ||
<file name="index.html"> | ||
<div ng-controller="MainCtrl as main"> | ||
Choose: | ||
<a href="Book/Moby">Moby</a> | | ||
<a href="Book/Moby/ch/1">Moby: Ch1</a> | | ||
<a href="Book/Gatsby">Gatsby</a> | | ||
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> | | ||
<a href="Book/Scarlet">Scarlet Letter</a><br/> | ||
* | ||
* - If the attribute is not set, disable scrolling. | ||
* - If the attribute is set without value, enable scrolling. | ||
* - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated | ||
* as an expression yields a truthy value. | ||
* @example | ||
<example name="ngView-directive" module="ngViewExample" | ||
deps="angular-route.js;angular-animate.js" | ||
animations="true" fixBase="true"> | ||
<file name="index.html"> | ||
<div ng-controller="MainCtrl as main"> | ||
Choose: | ||
<a href="Book/Moby">Moby</a> | | ||
<a href="Book/Moby/ch/1">Moby: Ch1</a> | | ||
<a href="Book/Gatsby">Gatsby</a> | | ||
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> | | ||
<a href="Book/Scarlet">Scarlet Letter</a><br/> | ||
<div class="view-animate-container"> | ||
<div ng-view class="view-animate"></div> | ||
</div> | ||
<hr /> | ||
<div class="view-animate-container"> | ||
<div ng-view class="view-animate"></div> | ||
</div> | ||
<hr /> | ||
<pre>$location.path() = {{main.$location.path()}}</pre> | ||
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre> | ||
<pre>$route.current.params = {{main.$route.current.params}}</pre> | ||
<pre>$routeParams = {{main.$routeParams}}</pre> | ||
</div> | ||
</file> | ||
<pre>$location.path() = {{main.$location.path()}}</pre> | ||
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre> | ||
<pre>$route.current.params = {{main.$route.current.params}}</pre> | ||
<pre>$routeParams = {{main.$routeParams}}</pre> | ||
</div> | ||
</file> | ||
<file name="book.html"> | ||
<div> | ||
controller: {{book.name}}<br /> | ||
Book Id: {{book.params.bookId}}<br /> | ||
</div> | ||
</file> | ||
<file name="book.html"> | ||
<div> | ||
controller: {{book.name}}<br /> | ||
Book Id: {{book.params.bookId}}<br /> | ||
</div> | ||
</file> | ||
<file name="chapter.html"> | ||
<div> | ||
controller: {{chapter.name}}<br /> | ||
Book Id: {{chapter.params.bookId}}<br /> | ||
Chapter Id: {{chapter.params.chapterId}} | ||
</div> | ||
</file> | ||
<file name="chapter.html"> | ||
<div> | ||
controller: {{chapter.name}}<br /> | ||
Book Id: {{chapter.params.bookId}}<br /> | ||
Chapter Id: {{chapter.params.chapterId}} | ||
</div> | ||
</file> | ||
<file name="animations.css"> | ||
.view-animate-container { | ||
<file name="animations.css"> | ||
.view-animate-container { | ||
position:relative; | ||
height:100px!important; | ||
position:relative; | ||
background:white; | ||
@@ -805,8 +797,7 @@ border:1px solid black; | ||
.view-animate { | ||
.view-animate { | ||
padding:10px; | ||
} | ||
.view-animate.ng-enter, .view-animate.ng-leave { | ||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; | ||
.view-animate.ng-enter, .view-animate.ng-leave { | ||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; | ||
@@ -826,17 +817,17 @@ | ||
.view-animate.ng-enter { | ||
.view-animate.ng-enter { | ||
left:100%; | ||
} | ||
.view-animate.ng-enter.ng-enter-active { | ||
.view-animate.ng-enter.ng-enter-active { | ||
left:0; | ||
} | ||
.view-animate.ng-leave.ng-leave-active { | ||
.view-animate.ng-leave.ng-leave-active { | ||
left:-100%; | ||
} | ||
</file> | ||
</file> | ||
<file name="script.js"> | ||
angular.module('ngViewExample', ['ngRoute', 'ngAnimate']) | ||
.config(['$routeProvider', '$locationProvider', | ||
function($routeProvider, $locationProvider) { | ||
<file name="script.js"> | ||
angular.module('ngViewExample', ['ngRoute', 'ngAnimate']) | ||
.config(['$routeProvider', '$locationProvider', | ||
function($routeProvider, $locationProvider) { | ||
$routeProvider | ||
@@ -856,4 +847,4 @@ .when('/Book/:bookId', { | ||
}]) | ||
.controller('MainCtrl', ['$route', '$routeParams', '$location', | ||
function($route, $routeParams, $location) { | ||
.controller('MainCtrl', ['$route', '$routeParams', '$location', | ||
function($route, $routeParams, $location) { | ||
this.$route = $route; | ||
@@ -863,7 +854,7 @@ this.$location = $location; | ||
}]) | ||
.controller('BookCtrl', ['$routeParams', function($routeParams) { | ||
.controller('BookCtrl', ['$routeParams', function($routeParams) { | ||
this.name = "BookCtrl"; | ||
this.params = $routeParams; | ||
}]) | ||
.controller('ChapterCtrl', ['$routeParams', function($routeParams) { | ||
.controller('ChapterCtrl', ['$routeParams', function($routeParams) { | ||
this.name = "ChapterCtrl"; | ||
@@ -873,6 +864,6 @@ this.params = $routeParams; | ||
</file> | ||
</file> | ||
<file name="protractor.js" type="protractor"> | ||
it('should load and compile correct template', function() { | ||
<file name="protractor.js" type="protractor"> | ||
it('should load and compile correct template', function() { | ||
element(by.linkText('Moby: Ch1')).click(); | ||
@@ -890,85 +881,85 @@ var content = element(by.css('[ng-view]')).getText(); | ||
}); | ||
</file> | ||
</example> | ||
*/ | ||
</file> | ||
</example> | ||
*/ | ||
/** | ||
* @ngdoc event | ||
* @name ngView#$viewContentLoaded | ||
* @eventType emit on the current ngView scope | ||
* @description | ||
* Emitted every time the ngView content is reloaded. | ||
*/ | ||
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate']; | ||
function ngViewFactory($route, $anchorScroll, $animate) { | ||
return { | ||
restrict: 'ECA', | ||
terminal: true, | ||
priority: 400, | ||
transclude: 'element', | ||
link: function (scope, $element, attr, ctrl, $transclude) { | ||
var currentScope, | ||
currentElement, | ||
previousLeaveAnimation, | ||
autoScrollExp = attr.autoscroll, | ||
onloadExp = attr.onload || ''; | ||
/** | ||
* @ngdoc event | ||
* @name ngView#$viewContentLoaded | ||
* @eventType emit on the current ngView scope | ||
* @description | ||
* Emitted every time the ngView content is reloaded. | ||
*/ | ||
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate']; | ||
function ngViewFactory($route, $anchorScroll, $animate) { | ||
return { | ||
restrict: 'ECA', | ||
terminal: true, | ||
priority: 400, | ||
transclude: 'element', | ||
link: function(scope, $element, attr, ctrl, $transclude) { | ||
var currentScope, | ||
currentElement, | ||
previousLeaveAnimation, | ||
autoScrollExp = attr.autoscroll, | ||
onloadExp = attr.onload || ''; | ||
scope.$on('$routeChangeSuccess', update); | ||
update(); | ||
scope.$on('$routeChangeSuccess', update); | ||
update(); | ||
function cleanupLastView() { | ||
if (previousLeaveAnimation) { | ||
$animate.cancel(previousLeaveAnimation); | ||
previousLeaveAnimation = null; | ||
} | ||
function cleanupLastView() { | ||
if (previousLeaveAnimation) { | ||
$animate.cancel(previousLeaveAnimation); | ||
previousLeaveAnimation = null; | ||
} | ||
if (currentScope) { | ||
currentScope.$destroy(); | ||
currentScope = null; | ||
} | ||
if (currentElement) { | ||
previousLeaveAnimation = $animate.leave(currentElement); | ||
previousLeaveAnimation.then(function () { | ||
previousLeaveAnimation = null; | ||
}); | ||
currentElement = null; | ||
} | ||
} | ||
if (currentScope) { | ||
currentScope.$destroy(); | ||
currentScope = null; | ||
} | ||
if (currentElement) { | ||
previousLeaveAnimation = $animate.leave(currentElement); | ||
previousLeaveAnimation.then(function() { | ||
previousLeaveAnimation = null; | ||
}); | ||
currentElement = null; | ||
} | ||
} | ||
function update() { | ||
var locals = $route.current && $route.current.locals, | ||
template = locals && locals.$template; | ||
function update() { | ||
var locals = $route.current && $route.current.locals, | ||
template = locals && locals.$template; | ||
if (angular.isDefined(template)) { | ||
var newScope = scope.$new(); | ||
var current = $route.current; | ||
if (angular.isDefined(template)) { | ||
var newScope = scope.$new(); | ||
var current = $route.current; | ||
// Note: This will also link all children of ng-view that were contained in the original | ||
// html. If that content contains controllers, ... they could pollute/change the scope. | ||
// However, using ng-view on an element with additional content does not make sense... | ||
// Note: We can't remove them in the cloneAttchFn of $transclude as that | ||
// function is called before linking the content, which would apply child | ||
// directives to non existing elements. | ||
var clone = $transclude(newScope, function (clone) { | ||
$animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() { | ||
if (angular.isDefined(autoScrollExp) | ||
&& (!autoScrollExp || scope.$eval(autoScrollExp))) { | ||
$anchorScroll(); | ||
} | ||
}); | ||
cleanupLastView(); | ||
}); | ||
// Note: This will also link all children of ng-view that were contained in the original | ||
// html. If that content contains controllers, ... they could pollute/change the scope. | ||
// However, using ng-view on an element with additional content does not make sense... | ||
// Note: We can't remove them in the cloneAttchFn of $transclude as that | ||
// function is called before linking the content, which would apply child | ||
// directives to non existing elements. | ||
var clone = $transclude(newScope, function(clone) { | ||
$animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() { | ||
if (angular.isDefined(autoScrollExp) | ||
&& (!autoScrollExp || scope.$eval(autoScrollExp))) { | ||
$anchorScroll(); | ||
} | ||
}); | ||
cleanupLastView(); | ||
}); | ||
currentElement = clone; | ||
currentScope = current.scope = newScope; | ||
currentScope.$emit('$viewContentLoaded'); | ||
currentScope.$eval(onloadExp); | ||
} else { | ||
cleanupLastView(); | ||
} | ||
} | ||
} | ||
}; | ||
currentElement = clone; | ||
currentScope = current.scope = newScope; | ||
currentScope.$emit('$viewContentLoaded'); | ||
currentScope.$eval(onloadExp); | ||
} else { | ||
cleanupLastView(); | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
@@ -980,31 +971,31 @@ // This directive is called during the $transclude call of the first `ngView` directive. | ||
// is called. | ||
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route']; | ||
function ngViewFillContentFactory($compile, $controller, $route) { | ||
return { | ||
restrict: 'ECA', | ||
priority: -400, | ||
link: function (scope, $element) { | ||
var current = $route.current, | ||
locals = current.locals; | ||
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route']; | ||
function ngViewFillContentFactory($compile, $controller, $route) { | ||
return { | ||
restrict: 'ECA', | ||
priority: -400, | ||
link: function(scope, $element) { | ||
var current = $route.current, | ||
locals = current.locals; | ||
$element.html(locals.$template); | ||
$element.html(locals.$template); | ||
var link = $compile($element.contents()); | ||
var link = $compile($element.contents()); | ||
if (current.controller) { | ||
locals.$scope = scope; | ||
var controller = $controller(current.controller, locals); | ||
if (current.controllerAs) { | ||
scope[current.controllerAs] = controller; | ||
} | ||
$element.data('$ngControllerController', controller); | ||
$element.children().data('$ngControllerController', controller); | ||
} | ||
if (current.controller) { | ||
locals.$scope = scope; | ||
var controller = $controller(current.controller, locals); | ||
if (current.controllerAs) { | ||
scope[current.controllerAs] = controller; | ||
} | ||
$element.data('$ngControllerController', controller); | ||
$element.children().data('$ngControllerController', controller); | ||
} | ||
link(scope); | ||
} | ||
}; | ||
link(scope); | ||
} | ||
}; | ||
} | ||
})(window, window.angular); |
@@ -95,13 +95,4 @@ (function () { | ||
.provider("visor", [function () { | ||
function addNextToUrl(url, $location, restrictedUrl) { | ||
if (config.shouldAddNext) { | ||
if (url.indexOf("?") >= 0) { | ||
return url.replace(/\?/, "?next=" + encodeURIComponent(restrictedUrl) + "&"); | ||
} | ||
return url + "?next=" + encodeURIComponent(restrictedUrl); | ||
} else { | ||
return url; | ||
} | ||
} | ||
var config = this; | ||
@@ -175,4 +166,18 @@ /** | ||
config.shouldAddNext = true; | ||
/** | ||
* @ngdoc property | ||
* @name visor.visorProvider#nextParameterName | ||
* @propertyOf visor.visorProvider | ||
* | ||
* @description | ||
* | ||
* The name of the parameter where the url that was restricted will be added to determine redirections | ||
* when using {@link visor.visorProvider#shouldAddNext `visorProvider.shouldAddNext`} | ||
* | ||
* See {@link visor.visorProvider#shouldAddNext `visorProvider.shouldAddNext`} for more. | ||
* | ||
* Defaults to 'next' | ||
*/ | ||
config.nextParameterName = 'next'; | ||
/** | ||
* @ngdoc function | ||
@@ -235,3 +240,6 @@ * @name visor.visorProvider#authenticate | ||
config.doOnNotAuthenticated = ["$location", "restrictedUrl", function ($location, restrictedUrl) { | ||
$location.url(addNextToUrl(config.loginRoute, $location, restrictedUrl)) | ||
$location.url(config.loginRoute); | ||
if (config.shouldAddNext) { | ||
$location.search(config.nextParameterName,restrictedUrl); | ||
} | ||
}]; | ||
@@ -261,3 +269,3 @@ /** | ||
config.doAfterManualAuthentication = ["$location", function ($location) { | ||
$location.url($location.search().next || config.homeRoute); | ||
$location.url($location.search()[config.nextParameterName] || config.homeRoute); | ||
}]; | ||
@@ -264,0 +272,0 @@ /** |
@@ -308,3 +308,2 @@ 'use strict'; | ||
$rootScope.$apply(); | ||
$timeout.flush(); | ||
//should redirect back to original route automatically | ||
@@ -329,2 +328,169 @@ expect($location.url()).toEqual("/private_url"); | ||
}); | ||
describe('next url',function(){ | ||
it('should add nextUrl to loginRoute with existing parameters', function () { | ||
angular.module("test.nextUrl.1", ['ui.router', 'visor']) | ||
.config(function ($stateProvider, visorProvider, authenticatedOnly) { | ||
$stateProvider.state("private", { | ||
url: "/private_url", | ||
restrict: authenticatedOnly | ||
}) | ||
.state("diffLogin", { | ||
url: "/diffLogin?name" | ||
}) | ||
visorProvider.loginRoute = "/diffLogin?name=myName#myHash" | ||
visorProvider.authenticate = function ($q) { | ||
return $q.reject("not authenticated"); | ||
}; | ||
}); | ||
module("test.nextUrl.1"); | ||
inject(function ($rootScope, $state, $q, $location, visor, $timeout) { | ||
$location.url("/private_url"); | ||
$rootScope.$apply(); | ||
$timeout.flush(); | ||
expect($state.current.name).toEqual("diffLogin"); | ||
expect($location.search().next).toEqual("/private_url"); | ||
expect($location.search().name).toEqual("myName"); | ||
expect($location.hash()).toEqual("myHash"); | ||
visor.setAuthenticated({username: "some_name"}); | ||
$rootScope.$apply(); | ||
//should redirect back to original route automatically | ||
expect($location.url()).toEqual("/private_url"); | ||
}); | ||
}); | ||
it('should add nextUrl to loginRoute if shouldAddNext option is disabled', function () { | ||
angular.module("test.nextUrl.2", ['ui.router', 'visor']) | ||
.config(function ($stateProvider, visorProvider, authenticatedOnly) { | ||
$stateProvider.state("private", { | ||
url: "/private_url", | ||
restrict: authenticatedOnly | ||
}) | ||
.state("login", { | ||
url: "/login" | ||
}) | ||
visorProvider.shouldAddNext = false; | ||
visorProvider.authenticate = function ($q) { | ||
return $q.reject("not authenticated"); | ||
}; | ||
}); | ||
module("test.nextUrl.2"); | ||
inject(function ($rootScope, $state, $q, $location, visor, $timeout) { | ||
$location.url("/private_url"); | ||
$rootScope.$apply(); | ||
$timeout.flush(); | ||
expect($state.current.name).toEqual("login"); | ||
expect($location.search().next).toBe(undefined); | ||
}); | ||
}); | ||
it('should override next parameter in loginUrl', function () { | ||
angular.module("test.nextUrl.3", ['ui.router', 'visor']) | ||
.config(function ($stateProvider, visorProvider, authenticatedOnly) { | ||
$stateProvider.state("private", { | ||
url: "/private_url", | ||
restrict: authenticatedOnly | ||
}) | ||
.state("login", { | ||
url: "/login?next" | ||
}) | ||
visorProvider.loginRoute = "/login?next=bad" | ||
visorProvider.authenticate = function ($q) { | ||
return $q.reject("not authenticated"); | ||
}; | ||
}); | ||
module("test.nextUrl.3"); | ||
inject(function ($rootScope, $state, $q, $location, visor, $timeout) { | ||
$location.url("/private_url"); | ||
$rootScope.$apply(); | ||
$timeout.flush(); | ||
expect($state.current.name).toEqual("login"); | ||
expect($location.search().next).toEqual("/private_url"); | ||
}); | ||
}); | ||
it('should not override next parameter in loginUrl if shouldAddNext is disabled', function () { | ||
angular.module("test.nextUrl.4", ['ui.router', 'visor']) | ||
.config(function ($stateProvider, visorProvider, authenticatedOnly) { | ||
$stateProvider.state("private", { | ||
url: "/private_url", | ||
restrict: authenticatedOnly | ||
}) | ||
.state("login", { | ||
url: "/login?next" | ||
}) | ||
visorProvider.loginRoute = "/login?next=bad" | ||
visorProvider.shouldAddNext = false; | ||
visorProvider.authenticate = function ($q) { | ||
return $q.reject("not authenticated"); | ||
}; | ||
}); | ||
module("test.nextUrl.4"); | ||
inject(function ($rootScope, $state, $q, $location, visor, $timeout) { | ||
$location.url("/private_url"); | ||
$rootScope.$apply(); | ||
$timeout.flush(); | ||
expect($state.current.name).toEqual("login"); | ||
expect($location.search().next).toEqual("bad"); | ||
}); | ||
}); | ||
it('should allow next parameter to be replaced with different name', function () { | ||
angular.module("test.nextUrl.5", ['ui.router', 'visor']) | ||
.config(function ($stateProvider, visorProvider, authenticatedOnly) { | ||
$stateProvider.state("private", { | ||
url: "/private_url", | ||
restrict: authenticatedOnly | ||
}) | ||
.state("login", { | ||
url: "/login?next" | ||
}) | ||
visorProvider.loginRoute = "/login?next=shouldStay" | ||
visorProvider.nextParameterName = "newNext" | ||
visorProvider.authenticate = function ($q) { | ||
return $q.reject("not authenticated"); | ||
}; | ||
}); | ||
module("test.nextUrl.5"); | ||
inject(function ($rootScope, $state, $q, $location, visor, $timeout) { | ||
$location.url("/private_url"); | ||
$rootScope.$apply(); | ||
$timeout.flush(); | ||
expect($state.current.name).toEqual("login"); | ||
expect($location.search().next).toEqual("shouldStay"); | ||
expect($location.search().newNext).toEqual("/private_url"); | ||
visor.setAuthenticated({username: "some_name"}); | ||
$rootScope.$apply(); | ||
//should redirect back to original route automatically | ||
expect($location.url()).toEqual("/private_url"); | ||
}); | ||
}); | ||
it('should allow changing next url manually', function () { | ||
angular.module("test.nextUrl.6", ['ui.router', 'visor']) | ||
.config(function ($stateProvider, visorProvider, authenticatedOnly,$urlRouterProvider) { | ||
$stateProvider.state("dashboard", { | ||
url: "/dashboard", | ||
restrict: authenticatedOnly | ||
}).state("private", { | ||
url: "/private", | ||
restrict: authenticatedOnly | ||
}) | ||
.state("login", { | ||
url: "/login" | ||
}) | ||
visorProvider.authenticate = function ($q) { | ||
return $q.reject("not authenticated"); | ||
}; | ||
}); | ||
module("test.nextUrl.6"); | ||
inject(function ($rootScope, $state, $q, $location, visor, $timeout) { | ||
$location.url("/dashboard"); | ||
$rootScope.$apply(); | ||
$timeout.flush(); | ||
expect($state.current.name).toEqual("login"); | ||
expect($location.search().next).toEqual("/dashboard"); | ||
$location.search('next','/private'); | ||
visor.setAuthenticated({username: "some_name"}); | ||
$rootScope.$apply(); | ||
//should redirect back to original route automatically | ||
expect($location.url()).toEqual("/private"); | ||
}); | ||
}); | ||
}) | ||
}); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
41278
1598811
Updatedangular@>= 1.2.0