%PDF- %PDF-
Direktori : /proc/985914/root/data/old/home/stash/stash/atlassian-stash/static/util/ |
Current File : //proc/985914/root/data/old/home/stash/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)); }; } /** * Pick a subset of the arguments to pass to a function. * Similar to arity but doesn't need to start at the first element * * @param {Function} fn * @param {number} start - The index of the first argument (inclusive). Defaults to 0 * @param {number} end - The index of the last argument (exclusive). Defaults to arguments.length * * @example * fn.argSlice(function(){return arguments}, 1)(1,2,3,4) // => [2, 3, 4] * fn.argSlice(function(){return arguments}, 1, 3)(1,2,3,4) // => [2, 3] * * @returns {Function} */ function argSlice(fn, start, end) { return function(){ return fn.apply(this, slice.call(arguments, start, end)); }; } /** * 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; }; } /** * Returns a function that when called will create a new instance of Clazz with the provided args * * @example * _.map(['Repo1','Repo2'], fn.create(Repository)) // => [new Repository('Repo1'), new Repository('Repo2')] * * @param Clazz * @returns {Function} */ function create(Clazz) { return function ctor() { var o = Object.create(Clazz.prototype); var ret = Clazz.apply(o, arguments); return ret !== null && typeof ret === 'object' ? ret : o; // if an object value is returned, use that instead. }; } /** * 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); var propDot = dot(keyPath); var selfDot = null; if (keyPath.indexOf('.') !== -1) { selfDot = dot(keyPath.substr(0, keyPath.lastIndexOf('.'))); } return function(object) { var prop = propDot(object); if (selfDot) { object = selfDot(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 compare each property on the input to the same property on a descriptor object. * Like _.where, but for a single item instead of a collection. * * @param {object} description * @returns {function(object): boolean} */ function propEqual(description) { // getters for each property var dotProps = Object.keys(description).map(dot); // property values for the reference object var descriptionProps = applyAll(dotProps, [description]); // functions that will take in an object and compare it to the reference object for a single property var comparators = _.zip(dotProps, descriptionProps).map(spread(function(dotProp, descProp) { return _.compose(eq(descProp), dotProp); })); return function(obj) { // return whether every property compares equally return comparators.every(lazyApply([obj])); }; } /** * Lazily set the value on an object for a given keyPath. * Supports '.' separated keyPaths * Optionally creates the keyPath as necessary. * * @param {string} keyPath * @param {*} value * @param {boolean} shouldCreatePath - Should `set` augment the object to satisfy the keyPath or should it throw a type error on invalid keyPaths? * * @example * var simple = {}; * set('test', 123)(simple) //=> simple == {test: 123} * * var deep = {one: { two: { three: 3}}}; * set('one.two.four', 4)(deep) //=> deep == {one: { two: { three: 3, four: 4}}} * * var deepEmpty = {} * set('one.two.four', 4)(deepEmpty) //=> TypeError * set('one.two.four', 4, true)(deepEmpty) //=> deepEmpty == {one: { two: { four: 4}}} * * @returns {Function} */ function set(keyPath, value, shouldCreatePath) { var keyParts = keyPath.split('.'); var last = keyParts.pop(); /** * @param {object} object */ return function(object){ keyParts.reduce(function(obj, propName) { if (obj[propName] != null && typeof obj[propName] !== 'object') { throw new TypeError("Can't set property on non-object"); } return (obj[propName] == null && shouldCreatePath) ? obj[propName] = {} : obj[propName]; }, object)[last] = value; }; } /** * 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.argSlice = argSlice; exports.constant = constant; exports.create = create; 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.propEqual = propEqual; exports.set = set; exports.spread = spread; exports.thisToParam = thisToParam; exports.unary = partialRight(arity, 1); exports.binary = partialRight(arity, 2); });