%PDF- %PDF-
| Direktori : /proc/self/root/data/old/home/stash/atlassian-stash/static/util/ |
| Current File : //proc/self/root/data/old/home/stash/atlassian-stash/static/util/function.js |
define('util/function', [
'underscore',
'exports'
],
/**
* Functional programming utils
*
* @exports util/function
*/
function(
_,
exports
) {
'use strict';
var slice = Array.prototype.slice;
/**
* Takes any number of predicate functions and returns a function that returns true if all the predicates return true
*
* @param {...Function} predicates - The predicate functions to combine
*
* @example
* var isPositive = function(a){ return a > 0; }
* var isEven = function(a){ return a % 2 === 0; }
* var isPositiveAndEven = and(isPositive, isEven)
*
* isPositiveAndEven(2) // => true
* isPositiveAndEven(1) // => false
* isPositiveAndEven(-2) // => false
*
* @returns {Function}
*/
function and(/*predicates*/){
var predicates = slice.call(arguments);
return function(/*arguments*/){
return _(predicates).every(lazyApply(arguments));
};
}
/**
* Invokes a list of functions with a given set of arguments.
*
* @param {Array<function>} list - list of functions
* @param {Array} args - list of arguments to apply
* @returns {Array} the results of applying args to each function
*/
function applyAll(list, args) {
return _.map(list, lazyApply.call(this, args));
}
/**
* Limit the number of arguments passed to a function.
* Used to trim off extra arguments from collection methods like `map` and `forEach`
* @param {Function} fn
* @param {number} numArgs
*
* @example
* fn.arity(function(){return arguments}, 2)(1,2,3) // => [1, 2]
*
* @returns {Function}
*/
function arity(fn, numArgs) {
return function(){
return fn.apply(this, slice.call(arguments, 0, numArgs));
};
}
/**
* Returned a function that when called _always_ returns the original argument.
* @param {*} arg
*
* @example
* _.map(['a', 'b', 'c'], fn.constant('x')) // => ['x', 'x', 'x']
*
* @returns {Function}
*/
function constant(arg) {
return function() {
return arg;
};
}
/**
* Return a function that when called with argument A, will return a default value if A is undefined or null, otherwise
* will return A.
* @param {*} theDefault
*
* @example
* _.map(['foo', 'bar', null, 'bar'], fn.defaultValue('foo')) // => ['foo', 'bar', 'foo', 'bar']
*
* @returns {Function}
*/
function defaultValue(theDefault) {
return function(a) {
return a != null ? a : theDefault;
};
}
/**
* Get a property from a lazy object.
* Basically a more generic version of _.pluck.
* Supports '.' separated keypaths e.g. 'user.avatar.size'
* Has null safety for keypaths (will return undefined if the keypath is invalid rather than throwing an exception)
* @param {string} keyPath
*
* @example
* var values = [{a: 'b'}, {a: 'c'}]
*
* _.map(values, fn.dot('a')) //=> ['b', 'c']
* _.map(values, _.compose(fn.eq('b'), fn.dot('a'))) // => [true, false]
*
*
* var obj = { my: { nested : { prop : 'foo' } } };
* dot('my.nested.prop')(obj) // => 'foo'
* dot('my.invalid.prop')(obj) // => undefined //invalid key path
*
* @returns {Function}
*/
function dot(keyPath) {
var keyParts = keyPath.split('.');
return function(object) {
return _(keyParts).reduce(function(obj, propName) {
return (obj != null) ? obj[propName] : undefined;
}, object);
};
}
/**
* Similar to invoke, but whereas invoke explicitly calls a function and returns undefined if the property is not one,
* dotX says that if the matched property is a function, eXecute it (with supplied args), otherwise return the property
* Basically a conditional dot + invoke
* Supports keypaths with null safety
*
* @param {string} keyPath
*
* @example
* dotX('myFunc')({myFunc: fn.constant('foo')}) // => 'foo'
*
* var obj = { my: { nested : { prop1 : 'foo', prop2: fn.constant('bar') } } };
* dotX('my.nested.prop1')(obj) // => 'foo'
* dotX('my.nested.prop2')(obj) // => 'bar'
* dotX('my.invalid.prop')(obj) // => undefined //invalid key path
*
* @returns {Function}
*/
function dotX(keyPath/*, args*/) {
var args = slice.call(arguments, 1);
return function(object) {
var prop = dot(keyPath)(object);
return (typeof prop === 'function') ? prop.apply(object, args) : prop;
};
}
/**
* Curried form of strict equals.
* @param {*} a
*
* @example
* _.map(['a', 'b', 'c'], fn.eq('a')) // => [true, false, false]
*
* @returns {Function}
*/
function eq(a) {
return function(b) {
return a === b;
};
}
/**
* Reverses the order of the function parameters
* @param {Function} fn
*
* @example
* fn.flip(function(){return arguments})(1,2,3) //=> [3, 2, 1]
*
* @returns {Function}
*/
function flip(fn) {
return function() {
return fn.apply(this, slice.call(arguments).reverse());
};
}
/**
* Wrapper for an `indexOf` type function that converts the result into a `found` boolean
*
* @param {Function} fn
*
* var myArray = [1,2,3,4];
* var foundInArray = found(myArray.indexOf.bind(myArray));
* foundInArray(1) // => true
* foundInArray(4) // => true
* foundInArray(5) // => false
*
* @returns {Function}
*/
function found(fn){
return function(/*arguments*/) {
var index = fn.apply(this, arguments);
return index >= 0;
};
}
/**
* Curried form of _.invoke that works with a single object.
* Useful when you are mapping over a collection and you want to call a method on each object that returns a value.
* Similar to `.map('.method')` in Bacon.
* Explicitly requires that the property is a function or throws a type error
* Also supports keypaths with null safety like fn.dot, however as mentioned above, if the final output of the
* keypath is undefined, invoke will throw a type error.
*
* @param {string} methodPath
*
* @example
* _.map([{isTrue: fn.constant(false)}, {isTrue: fn.constant(true)}], fn.invoke('isTrue')) // => [false, true]
*
* invoke('some.nested.prop')({some:{nested: {prop: fn.constant('bar')}}}) // => 'bar'
* invoke('some.nested.prop')({some:{nested: {prop: 'bar'}}}) // => Throws TypeError
* invoke('some.invalid.prop')({some:{nested: {prop: fn.constant('bar')}}}) // => Throws TypeError
*
* @returns {Function}
*/
function invoke(methodPath/*, args*/) {
var args = slice.call(arguments, 1);
return function(object) {
var fn = dot(methodPath)(object);
if (typeof fn !== 'function') {
throw new TypeError(fn + ' is not a function');
}
return fn.apply(object, args);
};
}
/**
* Returns a function that will apply the arguments to another function that is passed in.
* The use-case is usually related to mapping over a list of functions.
*
* @param {Array} args - array of arguments to apply
* @returns {Function}
*/
function lazyApply(args) {
return function(f) {
// Function.prototype.apply.call should work, but doesn't. :(
return f.apply(this, args);
};
}
/**
* The inverse of {@link dot}. Takes an object and returns a function for looking up keys in that object.
* @param {Object} map - object to lookup properties within.
*
* @example
* var myObj = {foo: 'bar', x:'y'};
*
* _.map(['foo', 'x'], lookup(myObj)) //=> ['bar', 'y']
*
* @returns {Function}
*/
function lookup(map) {
return function(key) {
return map[key];
};
}
/**
* Curries the application of any function to `!` and some arguments.
* In other words lazily inverts any function.
* @param {Function} fn
*
* @example
* _.map(['a', 'b', 'c'], fn.not(fn.eq('a'))) //=> [false, true, true]
*
* @returns {Function}
*/
function not(fn) {
return function(/*arguments*/) {
return !fn.apply(this, arguments);
};
}
/**
* Takes any number of predicate functions and returns a function that returns true if any of the predicates return true
*
* @param {...Function} predicates - The predicate functions to combine
*
* @example
* var isPositive = function(a){ return a > 0; }
* var isEven = function(a){ return a % 2 === 0; }
* var isPositiveOrEven = or(isPositive, isEven)
*
* isPositiveOrEven(2) // => true
* isPositiveOrEven(1) // => true
* isPositiveOrEven(-2) // => true
* isPositiveOrEven(-1) // => false
*
* @returns {Function}
*/
function or(/*predicates*/){
var predicates = slice.call(arguments);
return function(/*arguments*/){
return _(predicates).some(lazyApply(arguments));
};
}
/**
* Partially apply from the right rather than the left
* @param {Function} fn
*
* @example
* fn.partialRight(function(){return arguments}, 3, 4)(1, 2) //=> [1, 2, 3, 4]
*
* @returns {Function}
*/
function partialRight(fn /*, arguments*/) {
var partialArgs = slice.call(arguments, 1);
return function(){
return fn.apply(this, slice.call(arguments).concat(partialArgs));
};
}
/**
* Return a function that will call the passed function with one of the incoming arguments spread out (if it's an array).
* @param {Function} intoFn - the function to call with spread arguments
* @param {number} [index=0] - the index of the parameter to spread
*
* @example
* var varFunc = spread(func, 2);
*
* varFunc(1,2,[3,4],5) === func(1,2,3,4,5)
* varFunc(1,2,3,4,5) === func(1,2,3,4,5)
*
* @returns {Function}
*/
function spread(intoFn, index) {
return function() {
if (arguments.length <= index) {
return intoFn.apply(this, arguments);
}
// arguments.splice(index, 1, ...arguments[index])
var args = slice.call(arguments);
args.splice.apply(args, [index || 0, 1].concat(args[ index || 0]));
return intoFn.apply(this, args);
};
}
/**
* Map the context of a function call to a param.
* Mainly used for jQuery which sticks the target element in `this` for callbacks.
* This way you can `.bind` the callback to a different scope and still have easy access to the target element.
* It also makes it easier to reuse API methods as handlers.
* `fn` will be called with `this` as the first param, then the rest of the original arguments
* @param {Function} fn
*
* @example
* $(document).click(fn.thisToParam(function(){console.log(arguments)})) //=> [document, jQuery.Event]
*
* @returns {Function}
*/
function thisToParam(fn){
return function(/*arguments*/){
var args = slice.call(arguments);
args.unshift(this);
return fn.apply(this, args);
};
}
exports.and = and;
exports.applyAll = applyAll;
exports.arity = arity;
exports.constant = constant;
exports.defaultValue = defaultValue;
exports.dot = dot;
exports.dotX = dotX;
exports.eq = eq;
exports.flip = flip;
exports.found = found;
exports.invoke = invoke;
exports.lookup = lookup;
exports.lazyApply = lazyApply;
exports.not = not;
exports.or = or;
exports.partialRight = partialRight;
exports.spread = spread;
exports.thisToParam = thisToParam;
exports.unary = partialRight(arity, 1);
exports.binary = partialRight(arity, 2);
});