00001 var mapping = require('./_mapping'),
00002 fallbackHolder = require('./placeholder');
00003
00005 var push = Array.prototype.push;
00006
00016 function baseArity(func, n) {
00017 return n == 2
00018 ? function(a, b) { return func.apply(undefined, arguments); }
00019 : function(a) { return func.apply(undefined, arguments); };
00020 }
00021
00031 function baseAry(func, n) {
00032 return n == 2
00033 ? function(a, b) { return func(a, b); }
00034 : function(a) { return func(a); };
00035 }
00036
00044 function cloneArray(array) {
00045 var length = array ? array.length : 0,
00046 result = Array(length);
00047
00048 while (length--) {
00049 result[length] = array[length];
00050 }
00051 return result;
00052 }
00053
00061 function createCloner(func) {
00062 return function(object) {
00063 return func({}, object);
00064 };
00065 }
00066
00076 function flatSpread(func, start) {
00077 return function() {
00078 var length = arguments.length,
00079 lastIndex = length - 1,
00080 args = Array(length);
00081
00082 while (length--) {
00083 args[length] = arguments[length];
00084 }
00085 var array = args[start],
00086 otherArgs = args.slice(0, start);
00087
00088 if (array) {
00089 push.apply(otherArgs, array);
00090 }
00091 if (start != lastIndex) {
00092 push.apply(otherArgs, args.slice(start + 1));
00093 }
00094 return func.apply(this, otherArgs);
00095 };
00096 }
00097
00107 function wrapImmutable(func, cloner) {
00108 return function() {
00109 var length = arguments.length;
00110 if (!length) {
00111 return;
00112 }
00113 var args = Array(length);
00114 while (length--) {
00115 args[length] = arguments[length];
00116 }
00117 var result = args[0] = cloner.apply(undefined, args);
00118 func.apply(undefined, args);
00119 return result;
00120 };
00121 }
00122
00138 function baseConvert(util, name, func, options) {
00139 var setPlaceholder,
00140 isLib = typeof name == 'function',
00141 isObj = name === Object(name);
00142
00143 if (isObj) {
00144 options = func;
00145 func = name;
00146 name = undefined;
00147 }
00148 if (func == null) {
00149 throw new TypeError;
00150 }
00151 options || (options = {});
00152
00153 var config = {
00154 'cap': 'cap' in options ? options.cap : true,
00155 'curry': 'curry' in options ? options.curry : true,
00156 'fixed': 'fixed' in options ? options.fixed : true,
00157 'immutable': 'immutable' in options ? options.immutable : true,
00158 'rearg': 'rearg' in options ? options.rearg : true
00159 };
00160
00161 var forceCurry = ('curry' in options) && options.curry,
00162 forceFixed = ('fixed' in options) && options.fixed,
00163 forceRearg = ('rearg' in options) && options.rearg,
00164 placeholder = isLib ? func : fallbackHolder,
00165 pristine = isLib ? func.runInContext() : undefined;
00166
00167 var helpers = isLib ? func : {
00168 'ary': util.ary,
00169 'assign': util.assign,
00170 'clone': util.clone,
00171 'curry': util.curry,
00172 'forEach': util.forEach,
00173 'isArray': util.isArray,
00174 'isFunction': util.isFunction,
00175 'iteratee': util.iteratee,
00176 'keys': util.keys,
00177 'rearg': util.rearg,
00178 'toInteger': util.toInteger,
00179 'toPath': util.toPath
00180 };
00181
00182 var ary = helpers.ary,
00183 assign = helpers.assign,
00184 clone = helpers.clone,
00185 curry = helpers.curry,
00186 each = helpers.forEach,
00187 isArray = helpers.isArray,
00188 isFunction = helpers.isFunction,
00189 keys = helpers.keys,
00190 rearg = helpers.rearg,
00191 toInteger = helpers.toInteger,
00192 toPath = helpers.toPath;
00193
00194 var aryMethodKeys = keys(mapping.aryMethod);
00195
00196 var wrappers = {
00197 'castArray': function(castArray) {
00198 return function() {
00199 var value = arguments[0];
00200 return isArray(value)
00201 ? castArray(cloneArray(value))
00202 : castArray.apply(undefined, arguments);
00203 };
00204 },
00205 'iteratee': function(iteratee) {
00206 return function() {
00207 var func = arguments[0],
00208 arity = arguments[1],
00209 result = iteratee(func, arity),
00210 length = result.length;
00211
00212 if (config.cap && typeof arity == 'number') {
00213 arity = arity > 2 ? (arity - 2) : 1;
00214 return (length && length <= arity) ? result : baseAry(result, arity);
00215 }
00216 return result;
00217 };
00218 },
00219 'mixin': function(mixin) {
00220 return function(source) {
00221 var func = this;
00222 if (!isFunction(func)) {
00223 return mixin(func, Object(source));
00224 }
00225 var pairs = [];
00226 each(keys(source), function(key) {
00227 if (isFunction(source[key])) {
00228 pairs.push([key, func.prototype[key]]);
00229 }
00230 });
00231
00232 mixin(func, Object(source));
00233
00234 each(pairs, function(pair) {
00235 var value = pair[1];
00236 if (isFunction(value)) {
00237 func.prototype[pair[0]] = value;
00238 } else {
00239 delete func.prototype[pair[0]];
00240 }
00241 });
00242 return func;
00243 };
00244 },
00245 'nthArg': function(nthArg) {
00246 return function(n) {
00247 var arity = n < 0 ? 1 : (toInteger(n) + 1);
00248 return curry(nthArg(n), arity);
00249 };
00250 },
00251 'rearg': function(rearg) {
00252 return function(func, indexes) {
00253 var arity = indexes ? indexes.length : 0;
00254 return curry(rearg(func, indexes), arity);
00255 };
00256 },
00257 'runInContext': function(runInContext) {
00258 return function(context) {
00259 return baseConvert(util, runInContext(context), options);
00260 };
00261 }
00262 };
00263
00264
00265
00274 function castCap(name, func) {
00275 if (config.cap) {
00276 var indexes = mapping.iterateeRearg[name];
00277 if (indexes) {
00278 return iterateeRearg(func, indexes);
00279 }
00280 var n = !isLib && mapping.iterateeAry[name];
00281 if (n) {
00282 return iterateeAry(func, n);
00283 }
00284 }
00285 return func;
00286 }
00287
00297 function castCurry(name, func, n) {
00298 return (forceCurry || (config.curry && n > 1))
00299 ? curry(func, n)
00300 : func;
00301 }
00302
00312 function castFixed(name, func, n) {
00313 if (config.fixed && (forceFixed || !mapping.skipFixed[name])) {
00314 var data = mapping.methodSpread[name],
00315 start = data && data.start;
00316
00317 return start === undefined ? ary(func, n) : flatSpread(func, start);
00318 }
00319 return func;
00320 }
00321
00331 function castRearg(name, func, n) {
00332 return (config.rearg && n > 1 && (forceRearg || !mapping.skipRearg[name]))
00333 ? rearg(func, mapping.methodRearg[name] || mapping.aryRearg[n])
00334 : func;
00335 }
00336
00345 function cloneByPath(object, path) {
00346 path = toPath(path);
00347
00348 var index = -1,
00349 length = path.length,
00350 lastIndex = length - 1,
00351 result = clone(Object(object)),
00352 nested = result;
00353
00354 while (nested != null && ++index < length) {
00355 var key = path[index],
00356 value = nested[key];
00357
00358 if (value != null) {
00359 nested[path[index]] = clone(index == lastIndex ? value : Object(value));
00360 }
00361 nested = nested[key];
00362 }
00363 return result;
00364 }
00365
00373 function convertLib(options) {
00374 return _.runInContext.convert(options)(undefined);
00375 }
00376
00384 function createConverter(name, func) {
00385 var realName = mapping.aliasToReal[name] || name,
00386 methodName = mapping.remap[realName] || realName,
00387 oldOptions = options;
00388
00389 return function(options) {
00390 var newUtil = isLib ? pristine : helpers,
00391 newFunc = isLib ? pristine[methodName] : func,
00392 newOptions = assign(assign({}, oldOptions), options);
00393
00394 return baseConvert(newUtil, realName, newFunc, newOptions);
00395 };
00396 }
00397
00407 function iterateeAry(func, n) {
00408 return overArg(func, function(func) {
00409 return typeof func == 'function' ? baseAry(func, n) : func;
00410 });
00411 }
00412
00424 function iterateeRearg(func, indexes) {
00425 return overArg(func, function(func) {
00426 var n = indexes.length;
00427 return baseArity(rearg(baseAry(func, n), indexes), n);
00428 });
00429 }
00430
00439 function overArg(func, transform) {
00440 return function() {
00441 var length = arguments.length;
00442 if (!length) {
00443 return func();
00444 }
00445 var args = Array(length);
00446 while (length--) {
00447 args[length] = arguments[length];
00448 }
00449 var index = config.rearg ? 0 : (length - 1);
00450 args[index] = transform(args[index]);
00451 return func.apply(undefined, args);
00452 };
00453 }
00454
00464 function wrap(name, func) {
00465 var result,
00466 realName = mapping.aliasToReal[name] || name,
00467 wrapped = func,
00468 wrapper = wrappers[realName];
00469
00470 if (wrapper) {
00471 wrapped = wrapper(func);
00472 }
00473 else if (config.immutable) {
00474 if (mapping.mutate.array[realName]) {
00475 wrapped = wrapImmutable(func, cloneArray);
00476 }
00477 else if (mapping.mutate.object[realName]) {
00478 wrapped = wrapImmutable(func, createCloner(func));
00479 }
00480 else if (mapping.mutate.set[realName]) {
00481 wrapped = wrapImmutable(func, cloneByPath);
00482 }
00483 }
00484 each(aryMethodKeys, function(aryKey) {
00485 each(mapping.aryMethod[aryKey], function(otherName) {
00486 if (realName == otherName) {
00487 var data = mapping.methodSpread[realName],
00488 afterRearg = data && data.afterRearg;
00489
00490 result = afterRearg
00491 ? castFixed(realName, castRearg(realName, wrapped, aryKey), aryKey)
00492 : castRearg(realName, castFixed(realName, wrapped, aryKey), aryKey);
00493
00494 result = castCap(realName, result);
00495 result = castCurry(realName, result, aryKey);
00496 return false;
00497 }
00498 });
00499 return !result;
00500 });
00501
00502 result || (result = wrapped);
00503 if (result == func) {
00504 result = forceCurry ? curry(result, 1) : function() {
00505 return func.apply(this, arguments);
00506 };
00507 }
00508 result.convert = createConverter(realName, func);
00509 if (mapping.placeholder[realName]) {
00510 setPlaceholder = true;
00511 result.placeholder = func.placeholder = placeholder;
00512 }
00513 return result;
00514 }
00515
00516
00517
00518 if (!isObj) {
00519 return wrap(name, func);
00520 }
00521 var _ = func;
00522
00523
00524 var pairs = [];
00525 each(aryMethodKeys, function(aryKey) {
00526 each(mapping.aryMethod[aryKey], function(key) {
00527 var func = _[mapping.remap[key] || key];
00528 if (func) {
00529 pairs.push([key, wrap(key, func)]);
00530 }
00531 });
00532 });
00533
00534
00535 each(keys(_), function(key) {
00536 var func = _[key];
00537 if (typeof func == 'function') {
00538 var length = pairs.length;
00539 while (length--) {
00540 if (pairs[length][0] == key) {
00541 return;
00542 }
00543 }
00544 func.convert = createConverter(key, func);
00545 pairs.push([key, func]);
00546 }
00547 });
00548
00549
00550 each(pairs, function(pair) {
00551 _[pair[0]] = pair[1];
00552 });
00553
00554 _.convert = convertLib;
00555 if (setPlaceholder) {
00556 _.placeholder = placeholder;
00557 }
00558
00559 each(keys(_), function(key) {
00560 each(mapping.realToAlias[key] || [], function(alias) {
00561 _[alias] = _[key];
00562 });
00563 });
00564
00565 return _;
00566 }
00567
00568 module.exports = baseConvert;