1 var mapping = require('./_mapping'),
2 fallbackHolder = require('./placeholder');
4 /** Built-in value reference. */
5 var push = Array.prototype.push;
8 * Creates a function, with an arity of `n`, that invokes `func` with the
9 * arguments it receives.
12 * @param {Function} func The function to wrap.
13 * @param {number} n The arity of the new function.
14 * @returns {Function} Returns the new function.
16 function baseArity(func, n) {
18 ? function(a, b) { return func.apply(undefined, arguments); }
19 : function(a) { return func.apply(undefined, arguments); };
23 * Creates a function that invokes `func`, with up to `n` arguments, ignoring
24 * any additional arguments.
27 * @param {Function} func The function to cap arguments for.
28 * @param {number} n The arity cap.
29 * @returns {Function} Returns the new function.
31 function baseAry(func, n) {
33 ? function(a, b) { return func(a, b); }
34 : function(a) { return func(a); };
38 * Creates a clone of `array`.
41 * @param {Array} array The array to clone.
42 * @returns {Array} Returns the cloned array.
44 function cloneArray(array) {
45 var length = array ? array.length : 0,
46 result = Array(length);
49 result[length] = array[length];
55 * Creates a function that clones a given object using the assignment `func`.
58 * @param {Function} func The assignment function.
59 * @returns {Function} Returns the new cloner function.
61 function createCloner(func) {
62 return function(object) {
63 return func({}, object);
68 * This function is like `_.spread` except that it includes arguments after those spread.
71 * @param {Function} func The function to spread arguments over.
72 * @param {number} start The start position of the spread.
73 * @returns {Function} Returns the new function.
75 function spread(func, start) {
77 var length = arguments.length,
81 args[length] = arguments[length];
83 var array = args[start],
84 lastIndex = args.length - 1,
85 otherArgs = args.slice(0, start);
88 push.apply(otherArgs, array);
90 if (start != lastIndex) {
91 push.apply(otherArgs, args.slice(start + 1));
93 return func.apply(this, otherArgs);
98 * Creates a function that wraps `func` and uses `cloner` to clone the first
99 * argument it receives.
102 * @param {Function} func The function to wrap.
103 * @param {Function} cloner The function to clone arguments.
104 * @returns {Function} Returns the new immutable function.
106 function wrapImmutable(func, cloner) {
108 var length = arguments.length;
112 var args = Array(length);
114 args[length] = arguments[length];
116 var result = args[0] = cloner.apply(undefined, args);
117 func.apply(undefined, args);
123 * The base implementation of `convert` which accepts a `util` object of methods
124 * required to perform conversions.
126 * @param {Object} util The util object.
127 * @param {string} name The name of the function to convert.
128 * @param {Function} func The function to convert.
129 * @param {Object} [options] The options object.
130 * @param {boolean} [options.cap=true] Specify capping iteratee arguments.
131 * @param {boolean} [options.curry=true] Specify currying.
132 * @param {boolean} [options.fixed=true] Specify fixed arity.
133 * @param {boolean} [options.immutable=true] Specify immutable operations.
134 * @param {boolean} [options.rearg=true] Specify rearranging arguments.
135 * @returns {Function|Object} Returns the converted function or object.
137 function baseConvert(util, name, func, options) {
139 isLib = typeof name == 'function',
140 isObj = name === Object(name);
150 options || (options = {});
153 'cap': 'cap' in options ? options.cap : true,
154 'curry': 'curry' in options ? options.curry : true,
155 'fixed': 'fixed' in options ? options.fixed : true,
156 'immutable': 'immutable' in options ? options.immutable : true,
157 'rearg': 'rearg' in options ? options.rearg : true
160 var forceCurry = ('curry' in options) && options.curry,
161 forceFixed = ('fixed' in options) && options.fixed,
162 forceRearg = ('rearg' in options) && options.rearg,
163 placeholder = isLib ? func : fallbackHolder,
164 pristine = isLib ? func.runInContext() : undefined;
166 var helpers = isLib ? func : {
168 'assign': util.assign,
171 'forEach': util.forEach,
172 'isArray': util.isArray,
173 'isFunction': util.isFunction,
174 'iteratee': util.iteratee,
177 'toInteger': util.toInteger,
178 'toPath': util.toPath
181 var ary = helpers.ary,
182 assign = helpers.assign,
183 clone = helpers.clone,
184 curry = helpers.curry,
185 each = helpers.forEach,
186 isArray = helpers.isArray,
187 isFunction = helpers.isFunction,
189 rearg = helpers.rearg,
190 toInteger = helpers.toInteger,
191 toPath = helpers.toPath;
193 var aryMethodKeys = keys(mapping.aryMethod);
196 'castArray': function(castArray) {
198 var value = arguments[0];
199 return isArray(value)
200 ? castArray(cloneArray(value))
201 : castArray.apply(undefined, arguments);
204 'iteratee': function(iteratee) {
206 var func = arguments[0],
207 arity = arguments[1],
208 result = iteratee(func, arity),
209 length = result.length;
211 if (config.cap && typeof arity == 'number') {
212 arity = arity > 2 ? (arity - 2) : 1;
213 return (length && length <= arity) ? result : baseAry(result, arity);
218 'mixin': function(mixin) {
219 return function(source) {
221 if (!isFunction(func)) {
222 return mixin(func, Object(source));
225 each(keys(source), function(key) {
226 if (isFunction(source[key])) {
227 pairs.push([key, func.prototype[key]]);
231 mixin(func, Object(source));
233 each(pairs, function(pair) {
235 if (isFunction(value)) {
236 func.prototype[pair[0]] = value;
238 delete func.prototype[pair[0]];
244 'nthArg': function(nthArg) {
246 var arity = n < 0 ? 1 : (toInteger(n) + 1);
247 return curry(nthArg(n), arity);
250 'rearg': function(rearg) {
251 return function(func, indexes) {
252 var arity = indexes ? indexes.length : 0;
253 return curry(rearg(func, indexes), arity);
256 'runInContext': function(runInContext) {
257 return function(context) {
258 return baseConvert(util, runInContext(context), options);
263 /*--------------------------------------------------------------------------*/
266 * Casts `func` to a function with an arity capped iteratee if needed.
269 * @param {string} name The name of the function to inspect.
270 * @param {Function} func The function to inspect.
271 * @returns {Function} Returns the cast function.
273 function castCap(name, func) {
275 var indexes = mapping.iterateeRearg[name];
277 return iterateeRearg(func, indexes);
279 var n = !isLib && mapping.iterateeAry[name];
281 return iterateeAry(func, n);
288 * Casts `func` to a curried function if needed.
291 * @param {string} name The name of the function to inspect.
292 * @param {Function} func The function to inspect.
293 * @param {number} n The arity of `func`.
294 * @returns {Function} Returns the cast function.
296 function castCurry(name, func, n) {
297 return (forceCurry || (config.curry && n > 1))
303 * Casts `func` to a fixed arity function if needed.
306 * @param {string} name The name of the function to inspect.
307 * @param {Function} func The function to inspect.
308 * @param {number} n The arity cap.
309 * @returns {Function} Returns the cast function.
311 function castFixed(name, func, n) {
312 if (config.fixed && (forceFixed || !mapping.skipFixed[name])) {
313 var data = mapping.methodSpread[name],
314 start = data && data.start;
316 return start === undefined ? ary(func, n) : spread(func, start);
322 * Casts `func` to an rearged function if needed.
325 * @param {string} name The name of the function to inspect.
326 * @param {Function} func The function to inspect.
327 * @param {number} n The arity of `func`.
328 * @returns {Function} Returns the cast function.
330 function castRearg(name, func, n) {
331 return (config.rearg && n > 1 && (forceRearg || !mapping.skipRearg[name]))
332 ? rearg(func, mapping.methodRearg[name] || mapping.aryRearg[n])
337 * Creates a clone of `object` by `path`.
340 * @param {Object} object The object to clone.
341 * @param {Array|string} path The path to clone by.
342 * @returns {Object} Returns the cloned object.
344 function cloneByPath(object, path) {
348 length = path.length,
349 lastIndex = length - 1,
350 result = clone(Object(object)),
353 while (nested != null && ++index < length) {
354 var key = path[index],
358 nested[path[index]] = clone(index == lastIndex ? value : Object(value));
360 nested = nested[key];
366 * Converts `lodash` to an immutable auto-curried iteratee-first data-last
367 * version with conversion `options` applied.
369 * @param {Object} [options] The options object. See `baseConvert` for more details.
370 * @returns {Function} Returns the converted `lodash`.
372 function convertLib(options) {
373 return _.runInContext.convert(options)(undefined);
377 * Create a converter function for `func` of `name`.
379 * @param {string} name The name of the function to convert.
380 * @param {Function} func The function to convert.
381 * @returns {Function} Returns the new converter function.
383 function createConverter(name, func) {
384 var realName = mapping.aliasToReal[name] || name,
385 methodName = mapping.remap[realName] || realName,
386 oldOptions = options;
388 return function(options) {
389 var newUtil = isLib ? pristine : helpers,
390 newFunc = isLib ? pristine[methodName] : func,
391 newOptions = assign(assign({}, oldOptions), options);
393 return baseConvert(newUtil, realName, newFunc, newOptions);
398 * Creates a function that wraps `func` to invoke its iteratee, with up to `n`
399 * arguments, ignoring any additional arguments.
402 * @param {Function} func The function to cap iteratee arguments for.
403 * @param {number} n The arity cap.
404 * @returns {Function} Returns the new function.
406 function iterateeAry(func, n) {
407 return overArg(func, function(func) {
408 return typeof func == 'function' ? baseAry(func, n) : func;
413 * Creates a function that wraps `func` to invoke its iteratee with arguments
414 * arranged according to the specified `indexes` where the argument value at
415 * the first index is provided as the first argument, the argument value at
416 * the second index is provided as the second argument, and so on.
419 * @param {Function} func The function to rearrange iteratee arguments for.
420 * @param {number[]} indexes The arranged argument indexes.
421 * @returns {Function} Returns the new function.
423 function iterateeRearg(func, indexes) {
424 return overArg(func, function(func) {
425 var n = indexes.length;
426 return baseArity(rearg(baseAry(func, n), indexes), n);
431 * Creates a function that invokes `func` with its first argument transformed.
434 * @param {Function} func The function to wrap.
435 * @param {Function} transform The argument transform.
436 * @returns {Function} Returns the new function.
438 function overArg(func, transform) {
440 var length = arguments.length;
444 var args = Array(length);
446 args[length] = arguments[length];
448 var index = config.rearg ? 0 : (length - 1);
449 args[index] = transform(args[index]);
450 return func.apply(undefined, args);
455 * Creates a function that wraps `func` and applys the conversions
459 * @param {string} name The name of the function to wrap.
460 * @param {Function} func The function to wrap.
461 * @returns {Function} Returns the converted function.
463 function wrap(name, func) {
465 realName = mapping.aliasToReal[name] || name,
467 wrapper = wrappers[realName];
470 wrapped = wrapper(func);
472 else if (config.immutable) {
473 if (mapping.mutate.array[realName]) {
474 wrapped = wrapImmutable(func, cloneArray);
476 else if (mapping.mutate.object[realName]) {
477 wrapped = wrapImmutable(func, createCloner(func));
479 else if (mapping.mutate.set[realName]) {
480 wrapped = wrapImmutable(func, cloneByPath);
483 each(aryMethodKeys, function(aryKey) {
484 each(mapping.aryMethod[aryKey], function(otherName) {
485 if (realName == otherName) {
486 var spreadData = mapping.methodSpread[realName],
487 afterRearg = spreadData && spreadData.afterRearg;
490 ? castFixed(realName, castRearg(realName, wrapped, aryKey), aryKey)
491 : castRearg(realName, castFixed(realName, wrapped, aryKey), aryKey);
493 result = castCap(realName, result);
494 result = castCurry(realName, result, aryKey);
501 result || (result = wrapped);
502 if (result == func) {
503 result = forceCurry ? curry(result, 1) : function() {
504 return func.apply(this, arguments);
507 result.convert = createConverter(realName, func);
508 if (mapping.placeholder[realName]) {
509 setPlaceholder = true;
510 result.placeholder = func.placeholder = placeholder;
515 /*--------------------------------------------------------------------------*/
518 return wrap(name, func);
522 // Convert methods by ary cap.
524 each(aryMethodKeys, function(aryKey) {
525 each(mapping.aryMethod[aryKey], function(key) {
526 var func = _[mapping.remap[key] || key];
528 pairs.push([key, wrap(key, func)]);
533 // Convert remaining methods.
534 each(keys(_), function(key) {
536 if (typeof func == 'function') {
537 var length = pairs.length;
539 if (pairs[length][0] == key) {
543 func.convert = createConverter(key, func);
544 pairs.push([key, func]);
548 // Assign to `_` leaving `_.prototype` unchanged to allow chaining.
549 each(pairs, function(pair) {
550 _[pair[0]] = pair[1];
553 _.convert = convertLib;
554 if (setPlaceholder) {
555 _.placeholder = placeholder;
558 each(keys(_), function(key) {
559 each(mapping.realToAlias[key] || [], function(alias) {
567 module.exports = baseConvert;