todomvc
Version:
> Helping you select an MV\* framework
164 lines (139 loc) • 4.54 kB
JavaScript
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* functional
* Helper library for working with pure functions in wire and wire plugins
*
* NOTE: This lib assumes Function.prototype.bind is available
*
* wire is part of the cujo.js family of libraries (http://cujojs.com/)
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
(function (define) { 'use strict';
define(function (require) {
var when, slice;
when = require('when');
slice = [].slice;
/**
* Create a partial function
* @param f {Function}
* @param [args] {*} additional arguments will be bound to the returned partial
* @return {Function}
*/
function partial(f, args/*...*/) {
// What we want here is to allow the partial function to be called in
// any context, by attaching it to an object, or using partialed.call/apply
// That's why we're not using Function.bind() here. It has no way to bind
// arguments but allow the context to default. In other words, you MUST bind
// the the context to something with Function.bind().
// Optimization: return f if no args provided
if(arguments.length == 1) {
return f;
}
args = slice.call(arguments, 1);
return function() {
return f.apply(this, args.concat(slice.call(arguments)));
};
}
/**
* Compose functions
* @param funcs {Array} array of functions to compose
* @return {Function} composed function
*/
function compose(funcs) {
var first;
first = funcs[0];
funcs = funcs.slice(1);
return function composed() {
var context = this;
return funcs.reduce(function(result, f) {
return conditionalWhen(result, function(result) {
return f.call(context, result);
});
}, first.apply(this, arguments));
};
}
/**
* Parses the function composition string, resolving references as needed, and
* composes a function from the resolved refs.
* @param proxy {Object} wire proxy on which to invoke the final method of the composition
* @param composeString {String} function composition string
* of the form: 'transform1 | transform2 | ... | methodOnProxyTarget"
* @param {function} wire
* @param {function} wire.resolveRef function to use is resolving references, returns a promise
* @param {function} wire.getProxy function used to obtain a proxy for a component
* @return {Promise} a promise for the composed function
*/
compose.parse = function parseCompose(proxy, composeString, wire) {
var bindSpecs, resolveRef, getProxy;
if(typeof composeString != 'string') {
return wire(composeString).then(function(func) {
return createProxyInvoker(proxy, func);
});
}
bindSpecs = composeString.split(/\s*\|\s*/);
resolveRef = wire.resolveRef;
getProxy = wire.getProxy;
function createProxyInvoker(proxy, method) {
return function() {
return proxy.invoke(method, arguments);
};
}
function createBound(proxy, bindSpec) {
var target, method;
target = bindSpec.split('.');
if(target.length > 2) {
throw new Error('Only 1 "." is allowed in refs: ' + bindSpec);
}
if(target.length > 1) {
method = target[1];
target = target[0];
if(!target) {
return function(target) {
return target[method].apply(target, slice.call(arguments, 1));
};
}
return when(getProxy(target), function(proxy) {
return createProxyInvoker(proxy, method);
});
} else {
if(proxy && typeof proxy.get(bindSpec) == 'function') {
return createProxyInvoker(proxy, bindSpec);
} else {
return resolveRef(bindSpec);
}
}
}
// First, resolve each transform function, stuffing it into an array
// The result of this reduce will an array of concrete functions
// Then add the final context[method] to the array of funcs and
// return the composition.
return when.reduce(bindSpecs, function(funcs, bindSpec) {
return when(createBound(proxy, bindSpec), function(func) {
funcs.push(func);
return funcs;
});
}, []).then(
function(funcs) {
var context = proxy && proxy.target;
return (funcs.length == 1 ? funcs[0] : compose(funcs)).bind(context);
}
);
};
function conditionalWhen(promiseOrValue, onFulfill, onReject) {
return when.isPromise(promiseOrValue)
? when(promiseOrValue, onFulfill, onReject)
: onFulfill(promiseOrValue);
}
return {
compose: compose,
partial: partial
};
});
})(typeof define == 'function'
// AMD
? define
// CommonJS
: function(factory) { module.exports = factory(require); }
);