1 | var clone = (function() {
|
---|
2 | 'use strict';
|
---|
3 |
|
---|
4 | /**
|
---|
5 | * Clones (copies) an Object using deep copying.
|
---|
6 | *
|
---|
7 | * This function supports circular references by default, but if you are certain
|
---|
8 | * there are no circular references in your object, you can save some CPU time
|
---|
9 | * by calling clone(obj, false).
|
---|
10 | *
|
---|
11 | * Caution: if `circular` is false and `parent` contains circular references,
|
---|
12 | * your program may enter an infinite loop and crash.
|
---|
13 | *
|
---|
14 | * @param `parent` - the object to be cloned
|
---|
15 | * @param `circular` - set to true if the object to be cloned may contain
|
---|
16 | * circular references. (optional - true by default)
|
---|
17 | * @param `depth` - set to a number if the object is only to be cloned to
|
---|
18 | * a particular depth. (optional - defaults to Infinity)
|
---|
19 | * @param `prototype` - sets the prototype to be used when cloning an object.
|
---|
20 | * (optional - defaults to parent prototype).
|
---|
21 | */
|
---|
22 | function clone(parent, circular, depth, prototype) {
|
---|
23 | var filter;
|
---|
24 | if (typeof circular === 'object') {
|
---|
25 | depth = circular.depth;
|
---|
26 | prototype = circular.prototype;
|
---|
27 | filter = circular.filter;
|
---|
28 | circular = circular.circular
|
---|
29 | }
|
---|
30 | // maintain two arrays for circular references, where corresponding parents
|
---|
31 | // and children have the same index
|
---|
32 | var allParents = [];
|
---|
33 | var allChildren = [];
|
---|
34 |
|
---|
35 | var useBuffer = typeof Buffer != 'undefined';
|
---|
36 |
|
---|
37 | if (typeof circular == 'undefined')
|
---|
38 | circular = true;
|
---|
39 |
|
---|
40 | if (typeof depth == 'undefined')
|
---|
41 | depth = Infinity;
|
---|
42 |
|
---|
43 | // recurse this function so we don't reset allParents and allChildren
|
---|
44 | function _clone(parent, depth) {
|
---|
45 | // cloning null always returns null
|
---|
46 | if (parent === null)
|
---|
47 | return null;
|
---|
48 |
|
---|
49 | if (depth == 0)
|
---|
50 | return parent;
|
---|
51 |
|
---|
52 | var child;
|
---|
53 | var proto;
|
---|
54 | if (typeof parent != 'object') {
|
---|
55 | return parent;
|
---|
56 | }
|
---|
57 |
|
---|
58 | if (clone.__isArray(parent)) {
|
---|
59 | child = [];
|
---|
60 | } else if (clone.__isRegExp(parent)) {
|
---|
61 | child = new RegExp(parent.source, __getRegExpFlags(parent));
|
---|
62 | if (parent.lastIndex) child.lastIndex = parent.lastIndex;
|
---|
63 | } else if (clone.__isDate(parent)) {
|
---|
64 | child = new Date(parent.getTime());
|
---|
65 | } else if (useBuffer && Buffer.isBuffer(parent)) {
|
---|
66 | if (Buffer.allocUnsafe) {
|
---|
67 | // Node.js >= 4.5.0
|
---|
68 | child = Buffer.allocUnsafe(parent.length);
|
---|
69 | } else {
|
---|
70 | // Older Node.js versions
|
---|
71 | child = new Buffer(parent.length);
|
---|
72 | }
|
---|
73 | parent.copy(child);
|
---|
74 | return child;
|
---|
75 | } else {
|
---|
76 | if (typeof prototype == 'undefined') {
|
---|
77 | proto = Object.getPrototypeOf(parent);
|
---|
78 | child = Object.create(proto);
|
---|
79 | }
|
---|
80 | else {
|
---|
81 | child = Object.create(prototype);
|
---|
82 | proto = prototype;
|
---|
83 | }
|
---|
84 | }
|
---|
85 |
|
---|
86 | if (circular) {
|
---|
87 | var index = allParents.indexOf(parent);
|
---|
88 |
|
---|
89 | if (index != -1) {
|
---|
90 | return allChildren[index];
|
---|
91 | }
|
---|
92 | allParents.push(parent);
|
---|
93 | allChildren.push(child);
|
---|
94 | }
|
---|
95 |
|
---|
96 | for (var i in parent) {
|
---|
97 | var attrs;
|
---|
98 | if (proto) {
|
---|
99 | attrs = Object.getOwnPropertyDescriptor(proto, i);
|
---|
100 | }
|
---|
101 |
|
---|
102 | if (attrs && attrs.set == null) {
|
---|
103 | continue;
|
---|
104 | }
|
---|
105 | child[i] = _clone(parent[i], depth - 1);
|
---|
106 | }
|
---|
107 |
|
---|
108 | return child;
|
---|
109 | }
|
---|
110 |
|
---|
111 | return _clone(parent, depth);
|
---|
112 | }
|
---|
113 |
|
---|
114 | /**
|
---|
115 | * Simple flat clone using prototype, accepts only objects, usefull for property
|
---|
116 | * override on FLAT configuration object (no nested props).
|
---|
117 | *
|
---|
118 | * USE WITH CAUTION! This may not behave as you wish if you do not know how this
|
---|
119 | * works.
|
---|
120 | */
|
---|
121 | clone.clonePrototype = function clonePrototype(parent) {
|
---|
122 | if (parent === null)
|
---|
123 | return null;
|
---|
124 |
|
---|
125 | var c = function () {};
|
---|
126 | c.prototype = parent;
|
---|
127 | return new c();
|
---|
128 | };
|
---|
129 |
|
---|
130 | // private utility functions
|
---|
131 |
|
---|
132 | function __objToStr(o) {
|
---|
133 | return Object.prototype.toString.call(o);
|
---|
134 | };
|
---|
135 | clone.__objToStr = __objToStr;
|
---|
136 |
|
---|
137 | function __isDate(o) {
|
---|
138 | return typeof o === 'object' && __objToStr(o) === '[object Date]';
|
---|
139 | };
|
---|
140 | clone.__isDate = __isDate;
|
---|
141 |
|
---|
142 | function __isArray(o) {
|
---|
143 | return typeof o === 'object' && __objToStr(o) === '[object Array]';
|
---|
144 | };
|
---|
145 | clone.__isArray = __isArray;
|
---|
146 |
|
---|
147 | function __isRegExp(o) {
|
---|
148 | return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
|
---|
149 | };
|
---|
150 | clone.__isRegExp = __isRegExp;
|
---|
151 |
|
---|
152 | function __getRegExpFlags(re) {
|
---|
153 | var flags = '';
|
---|
154 | if (re.global) flags += 'g';
|
---|
155 | if (re.ignoreCase) flags += 'i';
|
---|
156 | if (re.multiline) flags += 'm';
|
---|
157 | return flags;
|
---|
158 | };
|
---|
159 | clone.__getRegExpFlags = __getRegExpFlags;
|
---|
160 |
|
---|
161 | return clone;
|
---|
162 | })();
|
---|
163 |
|
---|
164 | if (typeof module === 'object' && module.exports) {
|
---|
165 | module.exports = clone;
|
---|
166 | }
|
---|