Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enabled passing in helpers and partials to Mustache views, #260

Merged
merged 2 commits into from
Jan 29, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 36 additions & 16 deletions view/mustache/mustache.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function( can ){
STACK = '___st4ck',
// An alias for the most used context stacking call.
CONTEXT_STACK = STACK + '(' + CONTEXT + ',this)',
CONTEXT_OBJ = '{context:' + CONTEXT_STACK + ',options:options}',

/**
* Checks whether an object is a can.Observe.
Expand Down Expand Up @@ -57,9 +58,9 @@ function( can ){
// Support calling Mustache without the constructor.
// This returns a function that renders the template.
if ( this.constructor != Mustache ) {
var mustache = new Mustache(options, helpers);
return function(data, helpers) {
return mustache.render(data, helpers);
var mustache = new Mustache(options);
return function(data,options) {
return mustache.render(data,options);
};
}

Expand Down Expand Up @@ -99,12 +100,16 @@ function( can ){
* @param {Object} object data to be rendered
* @return {String} returns the result of the string
*/
render = function( object, extraHelpers ) {
render = function( object, options ) {
object = object || {};
for(var helper in extraHelpers){
Mustache.registerHelper(helper, extraHelpers[helper]);
options = options || {};
if(!options.helpers && !options.partials){
options.helpers = options;
}
return this.template.fn.call(object, object, { _data: object });
return this.template.fn.call(object, object, {
_data: object,
options: options
});
};

can.extend(Mustache.prototype, {
Expand Down Expand Up @@ -220,7 +225,8 @@ function( can ){
// Get the template name and call back into the render method,
// passing the name and the current context.
var templateName = can.trim(content.replace(/^>\s?/, '')).replace(/["|']/g, "");
return "can.Mustache.render('" + templateName + "', " + CONTEXT_STACK + ")";
return "options.partials && options.partials['"+templateName+"'] ? can.Mustache.renderPartial(options.partials['"+templateName+"']," +
CONTEXT_STACK + ".pop(),options) : can.Mustache.render('" + templateName + "', " + CONTEXT_STACK + ")";
}
},

Expand Down Expand Up @@ -506,7 +512,7 @@ function( can ){
});

// Start the content render block.
result.push('can.Mustache.txt(' + CONTEXT_STACK + ',' + (mode ? '"'+mode+'"' : 'null') + ',');
result.push('can.Mustache.txt('+CONTEXT_OBJ+',' + (mode ? '"'+mode+'"' : 'null') + ',');

// Iterate through the helper arguments, if there are any.
for (; arg = args[i]; i++) {
Expand All @@ -527,7 +533,7 @@ function( can ){
}

// Add the key/value.
result.push(m[4], ':', m[6] ? m[6] : 'can.Mustache.get("' + m[5].replace(/"/g,'\\"') + '",' + CONTEXT_STACK + ')');
result.push(m[4], ':', m[6] ? m[6] : 'can.Mustache.get("' + m[5].replace(/"/g,'\\"') + '",' + CONTEXT_OBJ + ')');

// Close the hash if this was the last argument.
if (i == args.length - 1) {
Expand All @@ -541,7 +547,7 @@ function( can ){
// Include the reference name.
arg.replace(/"/g,'\\"') + '",' +
// Then the stack of context.
CONTEXT_STACK +
CONTEXT_OBJ +
// Flag as a helper method to aid performance,
// if it is a known helper (anything with > 0 arguments).
(i == 0 && args.length > 1 ? ',true' : ',false') +
Expand Down Expand Up @@ -604,9 +610,14 @@ function( can ){
}].concat(mode ? args.pop() : []));


var extra = {};
if(context.context) {
extra = context.options;
context = context.context;
}

// Check for a registered helper or a helper-like function.
if (helper = (Mustache.getHelper(name) || (can.isFunction(name) && !name.isComputed && { fn: name }))) {
if (helper = (Mustache.getHelper(name,extra) || (can.isFunction(name) && !name.isComputed && { fn: name }))) {
// Use the most recent context as `this` for the helper.
var context = (context[STACK] && context[context.length - 1]) || context,
// Update the options with a function/inverse (the inner templates of a section).
Expand Down Expand Up @@ -733,6 +744,8 @@ function( can ){
* @param {Boolean} [isHelper] Whether the reference is a helper.
*/
Mustache.get = function(ref, contexts, isHelper, isArgument) {
var options = contexts.options || {};
contexts = contexts.context || contexts;
// Split the reference (like `a.b.c`) into an array of key names.
var names = ref.split('.'),
namesLength = names.length,
Expand Down Expand Up @@ -827,7 +840,7 @@ function( can ){
return obj[ref];
}
// Support helpers without arguments, but only if there wasn't a matching data reference.
else if (value = Mustache.getHelper(ref)) {
else if (value = Mustache.getHelper(ref,options)) {
return ref;
}

Expand Down Expand Up @@ -881,8 +894,10 @@ function( can ){
* @param {[type]} name of the helper
* @return {[type]} helper object
*/
Mustache.getHelper = function(name) {
return this._helpers[name]
Mustache.getHelper = function(name,options) {
return options && options.helpers && options.helpers[name] && {
fn: options.helpers[name]
} || this._helpers[name]
for (var i = 0, helper; helper = [i]; i++) {
// Find the correct helper
if (helper.name == name) {
Expand Down Expand Up @@ -921,7 +936,12 @@ function( can ){
// partial and context.
return can.view.render(partial, context);
};


Mustache.renderPartial = function(partial,context,options) {
return partial.render ? partial.render(context,options) :
partial(context,options);
};

// The built-in Mustache helpers.
can.each({
// Implements the `if` built-in helper.
Expand Down
1 change: 1 addition & 0 deletions view/mustache/test/fancy_name.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<span class="fancy">{{name}}</span>
1 change: 1 addition & 0 deletions view/mustache/test/hello.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p>Hello {{> name}}</p>
1 change: 1 addition & 0 deletions view/mustache/test/helper.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p>Hello {{cap name}}</p>
64 changes: 63 additions & 1 deletion view/mustache/test/mustache_test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
steal('funcunit/syn', 'can/view/mustache', 'can/model', function(){
steal('funcunit/syn', 'can/view/mustache', 'can/model', './hello.mustache', './fancy_name.mustache',
'./helper.mustache','./noglobals.mustache', function(_syn,_mustache,_model,hello,fancyName,helpers, noglobals){

module("can/view/mustache, rendering",{
setup : function(){
Expand Down Expand Up @@ -1525,4 +1526,65 @@ test("2 way binding helpers", function(){

})

test("can pass in partials",function() {
var div = document.createElement('div');
var result = hello({
name: "World"
},{
partials: {
name: fancyName
}
});
div.appendChild(result);

ok(/World/.test(div.innerHTML),"Hello World worked");
});


test("can pass in helpers",function() {
var div = document.createElement('div');
var result = helpers({
name: "world"
},{
helpers: {
cap: function(name) {
return can.capitalize(name);
}
}
});
div.appendChild(result);

ok(/World/.test(div.innerHTML),"Hello World worked");
});


test("avoid global helpers",function() {
var div = document.createElement('div'),
div2 = document.createElement('div');
var person = new can.Observe({
name: "Brian"
})
var result = noglobals({
person: person
},{
sometext: function(name){
return "Mr. "+name()
}
});
var result2 = noglobals({
person: person
},{
sometext: function(name){
return name()+" rules"
}
});
div.appendChild(result);
div2.appendChild(result2);

person.attr("name", "Ajax")

equal(div.innerHTML,"Mr. Ajax");
equal(div2.innerHTML,"Ajax rules");
});

});
1 change: 1 addition & 0 deletions view/mustache/test/noglobals.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{sometext person.name}}
9 changes: 6 additions & 3 deletions view/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -620,9 +620,12 @@ steal("can/util", function( can ) {
$view.cached[id] = new can.Deferred().resolve(function( data, helpers ) {
return renderer.call(data, data, helpers);
});
return function(){
return $view.frag(renderer.apply(this,arguments))
};
function frag(){
return $view.frag(renderer.apply(this,arguments));
}
// expose the renderer for mustache
frag.render = renderer;
return frag;
}

});
Expand Down