First pass of template-level API extensions

Untested, but a demo is in the works
This commit is contained in:
Geoff Schmidt
2012-08-08 17:05:36 -07:00
parent 7ba39d96cb
commit f766cf29fc
7 changed files with 176 additions and 23 deletions

View File

@@ -0,0 +1 @@
local

View File

@@ -0,0 +1,6 @@
# Meteor packages used by this project, one per line.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
autopublish

View File

@@ -0,0 +1,53 @@
Timers = new Meteor.Collection(null);
if (! Session.get("x")) {
Session.set("x", 1);
}
if (! Session.get("y")) {
Session.set("y", 1);
}
if (! Session.get("z")) {
Session.set("z", 1);
}
Template.redrawButtons.events = {
'click input.x': function () {
Session.set("x", Session.get("x") + 1);
},
'click input.y': function () {
Session.set("y", Session.get("y") + 1);
},
'click input.z': function () {
Session.set("z", Session.get("z") + 1);
}
};
Template.preserveDemo.preserve = [ '.input' ];
Template.preserveDemo.x =
Template.constantDemo.x =
Template.stateDemo.x =
function () {
return Session.get("x");
};
Template.stateDemo.events = {
'click .create': function () {
Timers.insert({});
}
};
Template.stateDemo.timers = function () {
return Timers.find();
};
Template.timer.events = {
'click .delete': function () {
Timers.remove(this._id);
}
};

View File

@@ -0,0 +1 @@
/* CSS declarations go here */

View File

@@ -0,0 +1,60 @@
<head>
<title>landmark-demo</title>
</head>
<body>
<h1>Landmark demo</h1>
{{> redrawButtons }}
{{> preserveDemo }}
<!--{{> constantDemo }} -->
{{> stateDemo }}
</body>
<template name="redrawButtons">
<input type="button" value="X++" class="x">
<input type="button" value="Y++" class="y">
<input type="button" value="Z++" class="z">
<br>
</template>
<template name="preserveDemo">
<h2>Simple element preservation</h2>
X={{x}}<br>
<input type="text"><br>
X={{x}}
</template>
<template name="constantDemo">
<h2>Constant regions</h2>
X={{x}}<br>
{{#constant}}
<div style="float: left; padding-right: 20px;">
<iframe width="200" height="200" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://maps.google.com/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=140+10th+Street,+San+Francisco,+CA&amp;aq=0&amp;oq=140+10th+s&amp;sll=37.7577,-122.4376&amp;sspn=0.166931,0.329247&amp;ie=UTF8&amp;hq=&amp;hnear=140+10th+St,+San+Francisco,+California+94103&amp;t=m&amp;ll=37.774921,-122.415419&amp;spn=0.013569,0.017252&amp;z=14&amp;iwloc=A&amp;output=embed"></iframe><br /><small><a href="https://maps.google.com/maps?f=q&amp;source=embed&amp;hl=en&amp;geocode=&amp;q=140+10th+Street,+San+Francisco,+CA&amp;aq=0&amp;oq=140+10th+s&amp;sll=37.7577,-122.4376&amp;sspn=0.166931,0.329247&amp;ie=UTF8&amp;hq=&amp;hnear=140+10th+St,+San+Francisco,+California+94103&amp;t=m&amp;ll=37.774921,-122.415419&amp;spn=0.013569,0.017252&amp;z=14&amp;iwloc=A" style="color:#0000FF;text-align:left">View Larger Map</a></small>
</div>
{{/constant}}
{{#constant}}
<div>
<iframe width="200" height="200" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://maps.google.com/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=880+Harrison+Street,+San+Francisco,+CA&amp;aq=0&amp;oq=880+harrison&amp;sll=37.7577,-122.4376&amp;sspn=0.166931,0.329247&amp;ie=UTF8&amp;hq=&amp;hnear=880+Harrison+St,+San+Francisco,+California+94107&amp;t=m&amp;ll=37.779534,-122.411213&amp;spn=0.013568,0.01708&amp;z=14&amp;iwloc=A&amp;output=embed"></iframe><br /><small><a href="https://maps.google.com/maps?f=q&amp;source=embed&amp;hl=en&amp;geocode=&amp;q=880+Harrison+Street,+San+Francisco,+CA&amp;aq=0&amp;oq=880+harrison&amp;sll=37.7577,-122.4376&amp;sspn=0.166931,0.329247&amp;ie=UTF8&amp;hq=&amp;hnear=880+Harrison+St,+San+Francisco,+California+94107&amp;t=m&amp;ll=37.779534,-122.411213&amp;spn=0.013568,0.01708&amp;z=14&amp;iwloc=A" style="color:#0000FF;text-align:left">View Larger Map</a></small>
</div>
{{/constant}}
X={{x}}
</template>
<template name="stateDemo">
<h2>Template callbacks</h2>
X={{x}}<br>
<input type="button" value="Create a timer" class="create"><br>
{{#each timers}}
<div>
{{> timer}}
Y={{y}}
</div>
{{/each}}
X={{x}}
</template>
<template name="timer">
<span class="elapsed"></span>
<input type="button" value="Delete" class="delete">
Z={{z}}
</template>

View File

@@ -21,10 +21,11 @@ Handlebars.json_ast_to_func = function (ast) {
//
// partials take one argument, data
// XXX handlebars' format for arguments is stupid. eg, options ===
// options.fn. plow this stuff under. treat block arguments (fn,
// inverse) as just another kind of argument, same as what is passed
// in via named arguments.
// XXX handlebars' format for arguments is not the clearest, likely
// for backwards compatibility to mustache. eg, options ===
// options.fn. take the opportunity to clean this up. treat block
// arguments (fn, inverse) as just another kind of argument, same as
// what is passed in via named arguments.
Handlebars._default_helpers = {
'with': function (data, options) {
return options.fn(data);

View File

@@ -27,11 +27,23 @@
);
};
Handlebars._default_helpers.constant = function(options) {
// XXX
};
_.extend(Handlebars._default_helpers, {
isolate: function (options) {
return Spark.isolate(function () {
return options.fn(this);
});
},
constant: function (options) {
return Spark.createLandmark({ constant: true }, function () {
return options.fn(this);
});
}
});
};
// map from landmark id, to the 'this' object for
// create/render/destroy callbacks on templates
var templateInstanceData = {};
Meteor._def_template = function (name, raw_func) {
Meteor._hook_handlebars();
@@ -44,23 +56,42 @@
// on which invocation of the partial this is.
var partial = function (data, branch) {
return Spark.labelBranch(branch, function () {
var html = Spark.isolate(function() {
return raw_func(data, {
helpers: partial,
partials: Meteor._partials,
name: name
});
});
var tmpl = name && Template[name] || {};
var t = name && Template[name];
if (t) {
html = Spark.attachEvents(t.events || {}, html);
html = Spark.createLandmark(
{ preserve: t.preserve || {} },
// XXX actually, we need to make this landmark available
// to Forms and execute the template here.
function(landmark) { return html; });
}
var html = Spark.createLandmark({
preserve: tmpl.preserve || {},
create: function () {
templateInstanceData[this.id] = {};
tmpl.create &&
tmpl.create.call(templateInstanceData[this.id]);
},
render: function () {
tmpl.render &&
tmpl.render.call(templateInstanceData[this.id], this);
},
destroy: function () {
tmpl.destroy &&
tmpl.destroy.call(templateInstanceData[this.id]);
delete templateInstanceData[this.id];
}
}, function (landmark) {
var html = Spark.isolate(function () {
// XXX Forms needs to run a hook before and after raw_func
// (and receive 'landmark')
return raw_func(data, {
helpers: partial,
partials: Meteor._partials,
name: name
});
});
// events need to be inside the landmark, not outside, so
// that when an event fires, you can retrieve the enclosing
// landmark to get the template data
if (tmpl.events)
html = Spark.attachEvents(tmpl.events, html);
return html;
});
html = Spark.setDataContext(data, html);
return html;