diff --git a/angularFiles.js b/angularFiles.js index cbc98db0b4d1..97a23f7810f2 100755 --- a/angularFiles.js +++ b/angularFiles.js @@ -5,6 +5,7 @@ var angularFiles = { 'src/minErr.js', 'src/Angular.js', 'src/loader.js', + 'src/shallowCopy.js', 'src/stringify.js', 'src/AngularPublic.js', 'src/jqLite.js', @@ -128,6 +129,7 @@ var angularFiles = { 'src/ngResource/resource.js' ], 'ngRoute': [ + 'src/shallowCopy.js', 'src/ngRoute/route.js', 'src/ngRoute/routeParams.js', 'src/ngRoute/directive/ngView.js' diff --git a/src/Angular.js b/src/Angular.js index bbb603f82f11..ddbe72f5600d 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -57,7 +57,6 @@ includes: true, arrayRemove: true, copy: true, - shallowCopy: true, equals: true, csp: true, jq: true, @@ -933,31 +932,6 @@ function copy(source, destination) { } } -/** - * Creates a shallow copy of an object, an array or a primitive. - * - * Assumes that there are no proto properties for objects. - */ -function shallowCopy(src, dst) { - if (isArray(src)) { - dst = dst || []; - - for (var i = 0, ii = src.length; i < ii; i++) { - dst[i] = src[i]; - } - } else if (isObject(src)) { - dst = dst || {}; - - for (var key in src) { - if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { - dst[key] = src[key]; - } - } - } - - return dst || src; -} - /** * @ngdoc function diff --git a/src/ngRoute/route.js b/src/ngRoute/route.js index cf5256c3dd51..c06c26475d1f 100644 --- a/src/ngRoute/route.js +++ b/src/ngRoute/route.js @@ -1,5 +1,11 @@ 'use strict'; +/* global shallowCopy: false */ + +// There are necessary for `shallowCopy()` (included via `src/shallowCopy.js`) +var isArray = angular.isArray; +var isObject = angular.isObject; + /** * @ngdoc module * @name ngRoute @@ -160,7 +166,7 @@ function $RouteProvider() { */ this.when = function(path, route) { //copy original route object to preserve params inherited from proto chain - var routeCopy = angular.copy(route); + var routeCopy = shallowCopy(route); if (angular.isUndefined(routeCopy.reloadOnSearch)) { routeCopy.reloadOnSearch = true; } diff --git a/src/shallowCopy.js b/src/shallowCopy.js new file mode 100644 index 000000000000..36602e55680e --- /dev/null +++ b/src/shallowCopy.js @@ -0,0 +1,28 @@ +'use strict'; + +/* global shallowCopy: true */ + +/** + * Creates a shallow copy of an object, an array or a primitive. + * + * Assumes that there are no proto properties for objects. + */ +function shallowCopy(src, dst) { + if (isArray(src)) { + dst = dst || []; + + for (var i = 0, ii = src.length; i < ii; i++) { + dst[i] = src[i]; + } + } else if (isObject(src)) { + dst = dst || {}; + + for (var key in src) { + if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { + dst[key] = src[key]; + } + } + } + + return dst || src; +} diff --git a/src/stringify.js b/src/stringify.js index 2a39da697048..ce07dffa4092 100644 --- a/src/stringify.js +++ b/src/stringify.js @@ -1,6 +1,6 @@ 'use strict'; -/* global: toDebugString: true */ +/* global toDebugString: true */ function serializeObject(obj) { var seen = []; diff --git a/test/ngRoute/routeSpec.js b/test/ngRoute/routeSpec.js index aa1d283fda83..aeb559eb5664 100644 --- a/test/ngRoute/routeSpec.js +++ b/test/ngRoute/routeSpec.js @@ -900,6 +900,87 @@ describe('$route', function() { }); + it('should not get affected by modifying the route definition object after route registration', + function() { + module(function($routeProvider) { + var rdo = {}; + + rdo.templateUrl = 'foo.html'; + $routeProvider.when('/foo', rdo); + + rdo.templateUrl = 'bar.html'; + $routeProvider.when('/bar', rdo); + }); + + inject(function($location, $rootScope, $route) { + $location.path('/bar'); + $rootScope.$digest(); + expect($location.path()).toBe('/bar'); + expect($route.current.templateUrl).toBe('bar.html'); + + $location.path('/foo'); + $rootScope.$digest(); + expect($location.path()).toBe('/foo'); + expect($route.current.templateUrl).toBe('foo.html'); + }); + } + ); + + + it('should use the property values of the passed in route definition object directly', + function() { + var $routeProvider; + + module(function(_$routeProvider_) { + $routeProvider = _$routeProvider_; + }); + + inject(function($location, $rootScope, $route, $sce) { + var sceWrappedUrl = $sce.trustAsResourceUrl('foo.html'); + $routeProvider.when('/foo', {templateUrl: sceWrappedUrl}); + + $location.path('/foo'); + $rootScope.$digest(); + expect($location.path()).toBe('/foo'); + expect($route.current.templateUrl).toBe(sceWrappedUrl); + }); + } + ); + + + it('should support custom `$sce` implementations', function() { + function MySafeResourceUrl(val) { + var self = this; + this._val = val; + this.getVal = function() { + return (this !== self) ? null : this._val; + }; + } + + var $routeProvider; + + module(function($provide, _$routeProvider_) { + $routeProvider = _$routeProvider_; + + $provide.decorator('$sce', function($delegate) { + $delegate.trustAsResourceUrl = function(url) { return new MySafeResourceUrl(url); }; + $delegate.getTrustedResourceUrl = function(v) { return v.getVal(); }; + $delegate.valueOf = function(v) { return v.getVal(); }; + return $delegate; + }); + }); + + inject(function($location, $rootScope, $route, $sce) { + $routeProvider.when('/foo', {templateUrl: $sce.trustAsResourceUrl('foo.html')}); + + $location.path('/foo'); + $rootScope.$digest(); + expect($location.path()).toBe('/foo'); + expect($sce.valueOf($route.current.templateUrl)).toBe('foo.html'); + }); + }); + + describe('redirection', function() { it('should support redirection via redirectTo property by updating $location', function() { module(function($routeProvider) {