UNPKG

8.06 kBJavaScriptView Raw
1/**
2 * lodash 3.0.3 (Custom Build) <https://lodash.com/>
3 * Build: `lodash modern modularize exports="npm" -o ./`
4 * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
5 * Based on Underscore.js 1.8.2 <http://underscorejs.org/LICENSE>
6 * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7 * Available under MIT license <https://lodash.com/license>
8 */
9var arrayCopy = require('lodash._arraycopy'),
10 arrayEach = require('lodash._arrayeach'),
11 baseFor = require('lodash._basefor'),
12 createAssigner = require('lodash._createassigner'),
13 isArguments = require('lodash.isarguments'),
14 isArray = require('lodash.isarray'),
15 isPlainObject = require('lodash.isplainobject'),
16 isTypedArray = require('lodash.istypedarray'),
17 keys = require('lodash.keys'),
18 toPlainObject = require('lodash.toplainobject');
19
20/**
21 * Checks if `value` is object-like.
22 *
23 * @private
24 * @param {*} value The value to check.
25 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
26 */
27function isObjectLike(value) {
28 return (value && typeof value == 'object') || false;
29}
30
31/**
32 * Used as the maximum length of an array-like value.
33 * See the [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer)
34 * for more details.
35 */
36var MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
37
38/**
39 * The base implementation of `_.forOwn` without support for callback
40 * shorthands and `this` binding.
41 *
42 * @private
43 * @param {Object} object The object to iterate over.
44 * @param {Function} iteratee The function invoked per iteration.
45 * @returns {Object} Returns `object`.
46 */
47function baseForOwn(object, iteratee) {
48 return baseFor(object, iteratee, keys);
49}
50
51/**
52 * The base implementation of `_.merge` without support for argument juggling,
53 * multiple sources, and `this` binding `customizer` functions.
54 *
55 * @private
56 * @param {Object} object The destination object.
57 * @param {Object} source The source object.
58 * @param {Function} [customizer] The function to customize merging properties.
59 * @param {Array} [stackA=[]] Tracks traversed source objects.
60 * @param {Array} [stackB=[]] Associates values with source counterparts.
61 * @returns {Object} Returns the destination object.
62 */
63function baseMerge(object, source, customizer, stackA, stackB) {
64 if (!isObject(object)) {
65 return object;
66 }
67 var isSrcArr = isLength(source.length) && (isArray(source) || isTypedArray(source));
68 (isSrcArr ? arrayEach : baseForOwn)(source, function(srcValue, key, source) {
69 if (isObjectLike(srcValue)) {
70 stackA || (stackA = []);
71 stackB || (stackB = []);
72 return baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
73 }
74 var value = object[key],
75 result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
76 isCommon = typeof result == 'undefined';
77
78 if (isCommon) {
79 result = srcValue;
80 }
81 if ((isSrcArr || typeof result != 'undefined') &&
82 (isCommon || (result === result ? (result !== value) : (value === value)))) {
83 object[key] = result;
84 }
85 });
86 return object;
87}
88
89/**
90 * A specialized version of `baseMerge` for arrays and objects which performs
91 * deep merges and tracks traversed objects enabling objects with circular
92 * references to be merged.
93 *
94 * @private
95 * @param {Object} object The destination object.
96 * @param {Object} source The source object.
97 * @param {string} key The key of the value to merge.
98 * @param {Function} mergeFunc The function to merge values.
99 * @param {Function} [customizer] The function to customize merging properties.
100 * @param {Array} [stackA=[]] Tracks traversed source objects.
101 * @param {Array} [stackB=[]] Associates values with source counterparts.
102 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
103 */
104function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
105 var length = stackA.length,
106 srcValue = source[key];
107
108 while (length--) {
109 if (stackA[length] == srcValue) {
110 object[key] = stackB[length];
111 return;
112 }
113 }
114 var value = object[key],
115 result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
116 isCommon = typeof result == 'undefined';
117
118 if (isCommon) {
119 result = srcValue;
120 if (isLength(srcValue.length) && (isArray(srcValue) || isTypedArray(srcValue))) {
121 result = isArray(value)
122 ? value
123 : (value ? arrayCopy(value) : []);
124 }
125 else if (isPlainObject(srcValue) || isArguments(srcValue)) {
126 result = isArguments(value)
127 ? toPlainObject(value)
128 : (isPlainObject(value) ? value : {});
129 }
130 else {
131 isCommon = false;
132 }
133 }
134 // Add the source value to the stack of traversed objects and associate
135 // it with its merged value.
136 stackA.push(srcValue);
137 stackB.push(result);
138
139 if (isCommon) {
140 // Recursively merge objects and arrays (susceptible to call stack limits).
141 object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
142 } else if (result === result ? (result !== value) : (value === value)) {
143 object[key] = result;
144 }
145}
146
147/**
148 * Checks if `value` is a valid array-like length.
149 *
150 * **Note:** This function is based on ES `ToLength`. See the
151 * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength)
152 * for more details.
153 *
154 * @private
155 * @param {*} value The value to check.
156 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
157 */
158function isLength(value) {
159 return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
160}
161
162/**
163 * Checks if `value` is the language type of `Object`.
164 * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
165 *
166 * **Note:** See the [ES5 spec](https://es5.github.io/#x8) for more details.
167 *
168 * @static
169 * @memberOf _
170 * @category Lang
171 * @param {*} value The value to check.
172 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
173 * @example
174 *
175 * _.isObject({});
176 * // => true
177 *
178 * _.isObject([1, 2, 3]);
179 * // => true
180 *
181 * _.isObject(1);
182 * // => false
183 */
184function isObject(value) {
185 // Avoid a V8 JIT bug in Chrome 19-20.
186 // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
187 var type = typeof value;
188 return type == 'function' || (value && type == 'object') || false;
189}
190
191/**
192 * Recursively merges own enumerable properties of the source object(s), that
193 * don't resolve to `undefined` into the destination object. Subsequent sources
194 * overwrite property assignments of previous sources. If `customizer` is
195 * provided it is invoked to produce the merged values of the destination and
196 * source properties. If `customizer` returns `undefined` merging is handled
197 * by the method instead. The `customizer` is bound to `thisArg` and invoked
198 * with five arguments; (objectValue, sourceValue, key, object, source).
199 *
200 * @static
201 * @memberOf _
202 * @category Object
203 * @param {Object} object The destination object.
204 * @param {...Object} [sources] The source objects.
205 * @param {Function} [customizer] The function to customize merging properties.
206 * @param {*} [thisArg] The `this` binding of `customizer`.
207 * @returns {Object} Returns `object`.
208 * @example
209 *
210 * var users = {
211 * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
212 * };
213 *
214 * var ages = {
215 * 'data': [{ 'age': 36 }, { 'age': 40 }]
216 * };
217 *
218 * _.merge(users, ages);
219 * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
220 *
221 * // using a customizer callback
222 * var object = {
223 * 'fruits': ['apple'],
224 * 'vegetables': ['beet']
225 * };
226 *
227 * var other = {
228 * 'fruits': ['banana'],
229 * 'vegetables': ['carrot']
230 * };
231 *
232 * _.merge(object, other, function(a, b) {
233 * if (_.isArray(a)) {
234 * return a.concat(b);
235 * }
236 * });
237 * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
238 */
239var merge = createAssigner(baseMerge);
240
241module.exports = merge;