UNPKG

todomvc

Version:

> Helping you select an MV\* framework

164 lines (139 loc) 4.54 kB
/** @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); } );