Skip to content

Commit 205637e

Browse files
author
mhughes
committed
Make copy 1) support recursive data structures and 2) support XML.
1 parent cbb3ce2 commit 205637e

File tree

2 files changed

+81
-30
lines changed

2 files changed

+81
-30
lines changed

src/Angular.js

+56-29
Original file line numberDiff line numberDiff line change
@@ -719,45 +719,72 @@ function isLeafNode (node) {
719719
</doc:source>
720720
</doc:example>
721721
*/
722-
function copy(source, destination){
722+
function copy(source, destination, recursiveKeys, recursiveValues){
723+
if (!recursiveKeys) {
724+
recursiveKeys = [];
725+
recursiveValues = [];
726+
}
727+
728+
function storePair(key, value) {
729+
recursiveKeys.push(key);
730+
recursiveValues.push(value);
731+
}
732+
723733
if (isWindow(source) || isScope(source)) {
724734
throw ngMinErr('cpws',
725735
"Can't copy! Making copies of Window or Scope instances is not supported.");
726736
}
727737

728-
if (!destination) {
729-
destination = source;
730-
if (source) {
731-
if (isArray(source)) {
732-
destination = copy(source, []);
733-
} else if (isDate(source)) {
734-
destination = new Date(source.getTime());
735-
} else if (isRegExp(source)) {
736-
destination = new RegExp(source.source);
737-
} else if (isObject(source)) {
738-
destination = copy(source, {});
739-
}
740-
}
741-
} else {
742-
if (source === destination) throw ngMinErr('cpi',
743-
"Can't copy! Source and destination are identical.");
744-
if (isArray(source)) {
745-
destination.length = 0;
746-
for ( var i = 0; i < source.length; i++) {
747-
destination.push(copy(source[i]));
738+
var existingSourceIndex = recursiveKeys.indexOf(source);
739+
if (existingSourceIndex === -1) {
740+
if (!destination) {
741+
destination = source;
742+
if (source) {
743+
if (isArray(source)) {
744+
destination = copy(source, [], recursiveKeys, recursiveValues);
745+
} else if (isDate(source)) {
746+
destination = new Date(source.getTime());
747+
storePair(source, destination);
748+
} else if (isRegExp(source)) {
749+
destination = new RegExp(source.source);
750+
storePair(source, destination);
751+
} else if (source.cloneNode) {
752+
destination = source.cloneNode();
753+
storePair(source, destination);
754+
} else if (isObject(source)) {
755+
destination = copy(source, {}, recursiveKeys, recursiveValues);
756+
} else {
757+
storePair(source, destination);
758+
}
759+
} else {
760+
storePair(source, destination);
748761
}
749762
} else {
750-
var h = destination.$$hashKey;
751-
forEach(destination, function(value, key){
752-
delete destination[key];
753-
});
754-
for ( var key in source) {
755-
destination[key] = copy(source[key]);
763+
if (source === destination) throw ngMinErr('cpi',
764+
"Can't copy! Source and destination are identical.");
765+
766+
storePair(source, destination);
767+
if (isArray(source)) {
768+
destination.length = 0;
769+
for ( var i = 0; i < source.length; i++) {
770+
destination.push(copy(source[i], null, recursiveKeys, recursiveValues));
771+
}
772+
} else {
773+
var h = destination.$$hashKey;
774+
forEach(destination, function(value, key){
775+
delete destination[key];
776+
});
777+
for ( var key in source) {
778+
destination[key] = copy(source[key], null, recursiveKeys, recursiveValues);
779+
}
780+
setHashKey(destination,h);
756781
}
757-
setHashKey(destination,h);
758782
}
783+
784+
return destination;
785+
} else {
786+
return recursiveValues[existingSourceIndex];
759787
}
760-
return destination;
761788
}
762789

763790
/**

test/AngularSpec.js

+25-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ describe('angular', function() {
8888
expect(dst.a).not.toBe(src.a);
8989
});
9090

91-
it("should deeply copy an object into a non-existing object", function() {
91+
it("should deeppy copy an object into a non-existing object", function() {
9292
var src = {a:{name:"value"}};
9393
var dst = copy(src, undefined);
9494
expect(src).toEqual({a:{name:"value"}});
@@ -106,6 +106,30 @@ describe('angular', function() {
106106
expect(copy([{key:null}])).toEqual([{key:null}]);
107107
});
108108

109+
it("should support recursive objects", function() {
110+
var recursiveObject = {
111+
foo: 1,
112+
bar: 1,
113+
recursive: null
114+
};
115+
recursiveObject.recursive = recursiveObject;
116+
117+
var expectedCopy = {
118+
foo: 1,
119+
bar: 1,
120+
recursive: recursiveObject
121+
}
122+
expect(copy(recursiveObject)).toEqual(expectedCopy);
123+
expect(copy(recursiveObject)).not.toBe(recursiveObject);
124+
});
125+
126+
it("should support XML nodes", function() {
127+
var anElement = document.createElement("foo");
128+
var theCopy = anElement.cloneNode();
129+
expect(copy(anElement)).toEqual(theCopy);
130+
expect(copy(anElement)).not.toBe(theCopy);
131+
});
132+
109133
it('should throw an exception if a Scope is being copied', inject(function($rootScope) {
110134
expect(function() { copy($rootScope.$new()); }).
111135
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");

0 commit comments

Comments
 (0)