can-stache
Version:
Live binding handlebars templates
139 lines (121 loc) • 3.83 kB
JavaScript
var canReflect = require("can-reflect");
var Observation = require("can-observation");
var live = require('can-view-live');
var expression = require("../src/expression");
var KeyObservable = require("../src/key-observable");
var bindAndRead = function (value) {
if ( value && canReflect.isValueLike(value) ) {
Observation.temporarilyBind(value);
return canReflect.getValue(value);
} else {
return value;
}
};
function forOfInteger(integer, variableName, options) {
var result = [];
for (var i = 0; i < integer; i++) {
var variableScope = {};
if(variableName !== undefined){
variableScope[variableName] = i;
}
result.push(
options.fn( options.scope
.add({ index: i }, { special: true })
.addLetContext(variableScope) )
);
}
return options.stringOnly ? result.join('') : result;
}
function forOfObject(object, variableName, options){
var result = [];
canReflect.each(object, function(val, key){
// Allow key to contain a dot, for example: "My.key.has.dot"
var value = new KeyObservable(object, key.replace(/\./g, "\\."));
var variableScope = {};
if(variableName !== undefined){
variableScope[variableName] = value;
}
result.push(
options.fn( options.scope
.add({ key: key }, { special: true })
.addLetContext(variableScope) )
);
});
return options.stringOnly ? result.join('') : result;
}
// this is called with the ast ... we are going to use that to our advantage.
var forHelper = function(helperOptions) {
// lookup
// TODO: remove in prod
// make sure we got called with the right stuff
if(helperOptions.exprData.argExprs.length !== 1) {
throw new Error("for(of) broken syntax");
}
// TODO: check if an instance of helper;
var helperExpr = helperOptions.exprData.argExprs[0].expr;
var variableName, valueLookup, valueObservable;
if(helperExpr instanceof expression.Lookup) {
valueObservable = helperExpr.value(helperOptions.scope);
} else if(helperExpr instanceof expression.Helper) {
// TODO: remove in prod
var inLookup = helperExpr.argExprs[0];
if(inLookup.key !== "of") {
throw new Error("for(of) broken syntax");
}
variableName = helperExpr.methodExpr.key;
valueLookup = helperExpr.argExprs[1];
valueObservable = valueLookup.value(helperOptions.scope);
}
var items = valueObservable;
var args = [].slice.call(arguments),
options = args.pop(),
resolved = bindAndRead(items);
if(resolved && resolved === Math.floor(resolved)) {
return forOfInteger(resolved, variableName, helperOptions);
}
if(resolved && !canReflect.isListLike(resolved)) {
return forOfObject(resolved,variableName, helperOptions);
}
if(options.stringOnly) {
var parts = [];
canReflect.eachIndex(resolved, function(value, index){
var variableScope = {};
if(variableName !== undefined){
variableScope[variableName] = value;
}
parts.push(
helperOptions.fn( options.scope
.add({ index: index }, { special: true })
.addLetContext(variableScope) )
);
});
return parts.join("");
} else {
// Tells that a helper has been called, this function should be returned through
// checking its value.
options.metadata.rendered = true;
return function(el){
var cb = function (item, index) {
var variableScope = {};
if(variableName !== undefined){
variableScope[variableName] = item;
}
return options.fn(
options.scope
.add({ index: index }, { special: true })
.addLetContext(variableScope),
options.options
);
};
live.list(el, items, cb, options.context, function(list){
return options.inverse(options.scope, options.options);
});
};
}
};
forHelper.isLiveBound = true;
forHelper.requiresOptionsArgument = true;
forHelper.ignoreArgLookup = function ignoreArgLookup(index) {
return index === 0;
};
module.exports = forHelper;