client/components/ianstormtaylor/reactive/0.13.2/lib/utils.js
/**
* Module dependencies.
*/
var debug = require('debug')('reactive:utils');
var props = require('component-props');
var adapter = require('./adapter');
/**
* Function cache.
*/
var cache = {};
/**
* Return interpolation property names in `str`,
* for example "{foo} and {bar}" would return
* ['foo', 'bar'].
*
* @param {String} str
* @return {Array}
* @api private
*/
exports.interpolationProps = function(str) {
var m;
var arr = [];
var re = /\{([^}]+)\}/g;
while (m = re.exec(str)) {
var expr = m[1];
arr = arr.concat(props(expr));
}
return unique(arr);
};
/**
* Interpolate `str` with the given `fn`.
*
* @param {String} str
* @param {Function} fn
* @return {String}
* @api private
*/
exports.interpolate = function(str, fn){
return str.replace(/\{([^}]+)\}/g, function(_, expr){
var cb = cache[expr];
if (!cb) cb = cache[expr] = compile(expr);
var val = fn(expr.trim(), cb);
return val == null ? '' : val;
});
};
/**
* Check if `str` has interpolation.
*
* @param {String} str
* @return {Boolean}
* @api private
*/
exports.hasInterpolation = function(str) {
return ~str.indexOf('{');
};
/**
* Remove computed properties notation from `str`.
*
* @param {String} str
* @return {String}
* @api private
*/
exports.clean = function(str) {
return str.split('<')[0].trim();
};
/**
* Call `prop` on `model` or `view`.
*
* @param {Object} model
* @param {Object} view
* @param {String} prop
* @return {Mixed}
* @api private
*/
exports.call = function(model, view, prop){
// view method
if ('function' == typeof view[prop]) {
return view[prop]();
}
// view value
if (view.hasOwnProperty(prop)) {
return view[prop];
}
// get property from model
return adapter.get(model, prop);
};
/**
* Compile `expr` to a `Function`.
*
* @param {String} expr
* @return {Function}
* @api private
*/
function compile(expr) {
// TODO: use props() callback instead
var re = /\.\w+|\w+ *\(|"[^"]*"|'[^']*'|\/([^/]+)\/|[a-zA-Z_]\w*/g;
var p = props(expr);
var body = expr.replace(re, function(_) {
if ('(' == _[_.length - 1]) return access(_);
if (!~p.indexOf(_)) return _;
return call(_);
});
debug('compile `%s`', body);
return new Function('model', 'view', 'call', 'return ' + body);
}
/**
* Access a method `prop` with dot notation.
*
* @param {String} prop
* @return {String}
* @api private
*/
function access(prop) {
prop = prop.replace('(', '');
return '(view.' + prop + ' '
+ '? view '
+ ': model).' + prop + '(';
}
/**
* Call `prop` on view, model, or access the model's property.
*
* @param {String} prop
* @return {String}
* @api private
*/
function call(prop) {
return 'call(model, view, "' + prop + '")';
}
/**
* Return unique array.
*
* @param {Array} arr
* @return {Array}
* @api private
*/
function unique(arr) {
var ret = [];
for (var i = 0; i < arr.length; i++) {
if (~ret.indexOf(arr[i])) continue;
ret.push(arr[i]);
}
return ret;
}