e7e631ce4bbb01d011e7835732470aec0709c02d
[motion.git] / public / bower_components / lodash / fp / _baseConvert.js
1 var mapping = require('./_mapping'),
2     mutateMap = mapping.mutate,
3     fallbackHolder = require('./placeholder');
4
5 /**
6  * Creates a function, with an arity of `n`, that invokes `func` with the
7  * arguments it receives.
8  *
9  * @private
10  * @param {Function} func The function to wrap.
11  * @param {number} n The arity of the new function.
12  * @returns {Function} Returns the new function.
13  */
14 function baseArity(func, n) {
15   return n == 2
16     ? function(a, b) { return func.apply(undefined, arguments); }
17     : function(a) { return func.apply(undefined, arguments); };
18 }
19
20 /**
21  * Creates a function that invokes `func`, with up to `n` arguments, ignoring
22  * any additional arguments.
23  *
24  * @private
25  * @param {Function} func The function to cap arguments for.
26  * @param {number} n The arity cap.
27  * @returns {Function} Returns the new function.
28  */
29 function baseAry(func, n) {
30   return n == 2
31     ? function(a, b) { return func(a, b); }
32     : function(a) { return func(a); };
33 }
34
35 /**
36  * Creates a clone of `array`.
37  *
38  * @private
39  * @param {Array} array The array to clone.
40  * @returns {Array} Returns the cloned array.
41  */
42 function cloneArray(array) {
43   var length = array ? array.length : 0,
44       result = Array(length);
45
46   while (length--) {
47     result[length] = array[length];
48   }
49   return result;
50 }
51
52 /**
53  * Creates a function that clones a given object using the assignment `func`.
54  *
55  * @private
56  * @param {Function} func The assignment function.
57  * @returns {Function} Returns the new cloner function.
58  */
59 function createCloner(func) {
60   return function(object) {
61     return func({}, object);
62   };
63 }
64
65 /**
66  * Creates a function that wraps `func` and uses `cloner` to clone the first
67  * argument it receives.
68  *
69  * @private
70  * @param {Function} func The function to wrap.
71  * @param {Function} cloner The function to clone arguments.
72  * @returns {Function} Returns the new immutable function.
73  */
74 function immutWrap(func, cloner) {
75   return function() {
76     var length = arguments.length;
77     if (!length) {
78       return result;
79     }
80     var args = Array(length);
81     while (length--) {
82       args[length] = arguments[length];
83     }
84     var result = args[0] = cloner.apply(undefined, args);
85     func.apply(undefined, args);
86     return result;
87   };
88 }
89
90 /**
91  * The base implementation of `convert` which accepts a `util` object of methods
92  * required to perform conversions.
93  *
94  * @param {Object} util The util object.
95  * @param {string} name The name of the function to convert.
96  * @param {Function} func The function to convert.
97  * @param {Object} [options] The options object.
98  * @param {boolean} [options.cap=true] Specify capping iteratee arguments.
99  * @param {boolean} [options.curry=true] Specify currying.
100  * @param {boolean} [options.fixed=true] Specify fixed arity.
101  * @param {boolean} [options.immutable=true] Specify immutable operations.
102  * @param {boolean} [options.rearg=true] Specify rearranging arguments.
103  * @returns {Function|Object} Returns the converted function or object.
104  */
105 function baseConvert(util, name, func, options) {
106   var setPlaceholder,
107       isLib = typeof name == 'function',
108       isObj = name === Object(name);
109
110   if (isObj) {
111     options = func;
112     func = name;
113     name = undefined;
114   }
115   if (func == null) {
116     throw new TypeError;
117   }
118   options || (options = {});
119
120   var config = {
121     'cap': 'cap' in options ? options.cap : true,
122     'curry': 'curry' in options ? options.curry : true,
123     'fixed': 'fixed' in options ? options.fixed : true,
124     'immutable': 'immutable' in options ? options.immutable : true,
125     'rearg': 'rearg' in options ? options.rearg : true
126   };
127
128   var forceCurry = ('curry' in options) && options.curry,
129       forceFixed = ('fixed' in options) && options.fixed,
130       forceRearg = ('rearg' in options) && options.rearg,
131       placeholder = isLib ? func : fallbackHolder,
132       pristine = isLib ? func.runInContext() : undefined;
133
134   var helpers = isLib ? func : {
135     'ary': util.ary,
136     'assign': util.assign,
137     'clone': util.clone,
138     'curry': util.curry,
139     'forEach': util.forEach,
140     'isArray': util.isArray,
141     'isFunction': util.isFunction,
142     'iteratee': util.iteratee,
143     'keys': util.keys,
144     'rearg': util.rearg,
145     'spread': util.spread,
146     'toPath': util.toPath
147   };
148
149   var ary = helpers.ary,
150       assign = helpers.assign,
151       clone = helpers.clone,
152       curry = helpers.curry,
153       each = helpers.forEach,
154       isArray = helpers.isArray,
155       isFunction = helpers.isFunction,
156       keys = helpers.keys,
157       rearg = helpers.rearg,
158       spread = helpers.spread,
159       toPath = helpers.toPath;
160
161   var aryMethodKeys = keys(mapping.aryMethod);
162
163   var wrappers = {
164     'castArray': function(castArray) {
165       return function() {
166         var value = arguments[0];
167         return isArray(value)
168           ? castArray(cloneArray(value))
169           : castArray.apply(undefined, arguments);
170       };
171     },
172     'iteratee': function(iteratee) {
173       return function() {
174         var func = arguments[0],
175             arity = arguments[1],
176             result = iteratee(func, arity),
177             length = result.length;
178
179         if (config.cap && typeof arity == 'number') {
180           arity = arity > 2 ? (arity - 2) : 1;
181           return (length && length <= arity) ? result : baseAry(result, arity);
182         }
183         return result;
184       };
185     },
186     'mixin': function(mixin) {
187       return function(source) {
188         var func = this;
189         if (!isFunction(func)) {
190           return mixin(func, Object(source));
191         }
192         var methods = [],
193             methodNames = [];
194
195         each(keys(source), function(key) {
196           var value = source[key];
197           if (isFunction(value)) {
198             methodNames.push(key);
199             methods.push(func.prototype[key]);
200           }
201         });
202
203         mixin(func, Object(source));
204
205         each(methodNames, function(methodName, index) {
206           var method = methods[index];
207           if (isFunction(method)) {
208             func.prototype[methodName] = method;
209           } else {
210             delete func.prototype[methodName];
211           }
212         });
213         return func;
214       };
215     },
216     'runInContext': function(runInContext) {
217       return function(context) {
218         return baseConvert(util, runInContext(context), options);
219       };
220     }
221   };
222
223   /*--------------------------------------------------------------------------*/
224
225   /**
226    * Creates a clone of `object` by `path`.
227    *
228    * @private
229    * @param {Object} object The object to clone.
230    * @param {Array|string} path The path to clone by.
231    * @returns {Object} Returns the cloned object.
232    */
233   function cloneByPath(object, path) {
234     path = toPath(path);
235
236     var index = -1,
237         length = path.length,
238         result = clone(Object(object)),
239         nested = result;
240
241     while (nested != null && ++index < length) {
242       var key = path[index],
243           value = nested[key];
244
245       if (value != null) {
246         nested[key] = clone(Object(value));
247       }
248       nested = nested[key];
249     }
250     return result;
251   }
252
253   /**
254    * Converts `lodash` to an immutable auto-curried iteratee-first data-last
255    * version with conversion `options` applied.
256    *
257    * @param {Object} [options] The options object. See `baseConvert` for more details.
258    * @returns {Function} Returns the converted `lodash`.
259    */
260   function convertLib(options) {
261     return _.runInContext.convert(options)(undefined);
262   }
263
264   /**
265    * Create a converter function for `func` of `name`.
266    *
267    * @param {string} name The name of the function to convert.
268    * @param {Function} func The function to convert.
269    * @returns {Function} Returns the new converter function.
270    */
271   function createConverter(name, func) {
272     var oldOptions = options;
273     return function(options) {
274       var newUtil = isLib ? pristine : helpers,
275           newFunc = isLib ? pristine[name] : func,
276           newOptions = assign(assign({}, oldOptions), options);
277
278       return baseConvert(newUtil, name, newFunc, newOptions);
279     };
280   }
281
282   /**
283    * Creates a function that wraps `func` to invoke its iteratee, with up to `n`
284    * arguments, ignoring any additional arguments.
285    *
286    * @private
287    * @param {Function} func The function to cap iteratee arguments for.
288    * @param {number} n The arity cap.
289    * @returns {Function} Returns the new function.
290    */
291   function iterateeAry(func, n) {
292     return overArg(func, function(func) {
293       return typeof func == 'function' ? baseAry(func, n) : func;
294     });
295   }
296
297   /**
298    * Creates a function that wraps `func` to invoke its iteratee with arguments
299    * arranged according to the specified `indexes` where the argument value at
300    * the first index is provided as the first argument, the argument value at
301    * the second index is provided as the second argument, and so on.
302    *
303    * @private
304    * @param {Function} func The function to rearrange iteratee arguments for.
305    * @param {number[]} indexes The arranged argument indexes.
306    * @returns {Function} Returns the new function.
307    */
308   function iterateeRearg(func, indexes) {
309     return overArg(func, function(func) {
310       var n = indexes.length;
311       return baseArity(rearg(baseAry(func, n), indexes), n);
312     });
313   }
314
315   /**
316    * Creates a function that invokes `func` with its first argument passed
317    * thru `transform`.
318    *
319    * @private
320    * @param {Function} func The function to wrap.
321    * @param {...Function} transform The functions to transform the first argument.
322    * @returns {Function} Returns the new function.
323    */
324   function overArg(func, transform) {
325     return function() {
326       var length = arguments.length;
327       if (!length) {
328         return func();
329       }
330       var args = Array(length);
331       while (length--) {
332         args[length] = arguments[length];
333       }
334       var index = config.rearg ? 0 : (length - 1);
335       args[index] = transform(args[index]);
336       return func.apply(undefined, args);
337     };
338   }
339
340   /**
341    * Creates a function that wraps `func` and applys the conversions
342    * rules by `name`.
343    *
344    * @private
345    * @param {string} name The name of the function to wrap.
346    * @param {Function} func The function to wrap.
347    * @returns {Function} Returns the converted function.
348    */
349   function wrap(name, func) {
350     name = mapping.aliasToReal[name] || name;
351
352     var result,
353         wrapped = func,
354         wrapper = wrappers[name];
355
356     if (wrapper) {
357       wrapped = wrapper(func);
358     }
359     else if (config.immutable) {
360       if (mutateMap.array[name]) {
361         wrapped = immutWrap(func, cloneArray);
362       }
363       else if (mutateMap.object[name]) {
364         wrapped = immutWrap(func, createCloner(func));
365       }
366       else if (mutateMap.set[name]) {
367         wrapped = immutWrap(func, cloneByPath);
368       }
369     }
370     each(aryMethodKeys, function(aryKey) {
371       each(mapping.aryMethod[aryKey], function(otherName) {
372         if (name == otherName) {
373           var aryN = !isLib && mapping.iterateeAry[name],
374               reargIndexes = mapping.iterateeRearg[name],
375               spreadStart = mapping.methodSpread[name];
376
377           result = wrapped;
378           if (config.fixed && (forceFixed || !mapping.skipFixed[name])) {
379             result = spreadStart === undefined
380               ? ary(result, aryKey)
381               : spread(result, spreadStart);
382           }
383           if (config.rearg && aryKey > 1 && (forceRearg || !mapping.skipRearg[name])) {
384             result = rearg(result, mapping.methodRearg[name] || mapping.aryRearg[aryKey]);
385           }
386           if (config.cap) {
387             if (reargIndexes) {
388               result = iterateeRearg(result, reargIndexes);
389             } else if (aryN) {
390               result = iterateeAry(result, aryN);
391             }
392           }
393           if (forceCurry || (config.curry && aryKey > 1)) {
394             forceCurry  && console.log(forceCurry, name);
395             result = curry(result, aryKey);
396           }
397           return false;
398         }
399       });
400       return !result;
401     });
402
403     result || (result = wrapped);
404     if (result == func) {
405       result = forceCurry ? curry(result, 1) : function() {
406         return func.apply(this, arguments);
407       };
408     }
409     result.convert = createConverter(name, func);
410     if (mapping.placeholder[name]) {
411       setPlaceholder = true;
412       result.placeholder = func.placeholder = placeholder;
413     }
414     return result;
415   }
416
417   /*--------------------------------------------------------------------------*/
418
419   if (!isObj) {
420     return wrap(name, func);
421   }
422   var _ = func;
423
424   // Convert methods by ary cap.
425   var pairs = [];
426   each(aryMethodKeys, function(aryKey) {
427     each(mapping.aryMethod[aryKey], function(key) {
428       var func = _[mapping.remap[key] || key];
429       if (func) {
430         pairs.push([key, wrap(key, func)]);
431       }
432     });
433   });
434
435   // Convert remaining methods.
436   each(keys(_), function(key) {
437     var func = _[key];
438     if (typeof func == 'function') {
439       var length = pairs.length;
440       while (length--) {
441         if (pairs[length][0] == key) {
442           return;
443         }
444       }
445       func.convert = createConverter(key, func);
446       pairs.push([key, func]);
447     }
448   });
449
450   // Assign to `_` leaving `_.prototype` unchanged to allow chaining.
451   each(pairs, function(pair) {
452     _[pair[0]] = pair[1];
453   });
454
455   _.convert = convertLib;
456   if (setPlaceholder) {
457     _.placeholder = placeholder;
458   }
459   // Assign aliases.
460   each(keys(_), function(key) {
461     each(mapping.realToAlias[key] || [], function(alias) {
462       _[alias] = _[key];
463     });
464   });
465
466   return _;
467 }
468
469 module.exports = baseConvert;