From f766cf29fc140ffaeff735f1e155ff31d13c7ae3 Mon Sep 17 00:00:00 2001 From: Geoff Schmidt Date: Wed, 8 Aug 2012 17:05:36 -0700 Subject: [PATCH] First pass of template-level API extensions Untested, but a demo is in the works --- examples/landmark-demo/.meteor/.gitignore | 1 + examples/landmark-demo/.meteor/packages | 6 ++ .../landmark-demo/client/landmark-demo.js | 53 ++++++++++++++ examples/landmark-demo/landmark-demo.css | 1 + examples/landmark-demo/landmark-demo.html | 60 ++++++++++++++++ packages/handlebars/evaluate.js | 9 +-- packages/templating/deftemplate.js | 69 ++++++++++++++----- 7 files changed, 176 insertions(+), 23 deletions(-) create mode 100644 examples/landmark-demo/.meteor/.gitignore create mode 100644 examples/landmark-demo/.meteor/packages create mode 100644 examples/landmark-demo/client/landmark-demo.js create mode 100644 examples/landmark-demo/landmark-demo.css create mode 100644 examples/landmark-demo/landmark-demo.html diff --git a/examples/landmark-demo/.meteor/.gitignore b/examples/landmark-demo/.meteor/.gitignore new file mode 100644 index 0000000000..4083037423 --- /dev/null +++ b/examples/landmark-demo/.meteor/.gitignore @@ -0,0 +1 @@ +local diff --git a/examples/landmark-demo/.meteor/packages b/examples/landmark-demo/.meteor/packages new file mode 100644 index 0000000000..12c5f051c0 --- /dev/null +++ b/examples/landmark-demo/.meteor/packages @@ -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 diff --git a/examples/landmark-demo/client/landmark-demo.js b/examples/landmark-demo/client/landmark-demo.js new file mode 100644 index 0000000000..8af45035c5 --- /dev/null +++ b/examples/landmark-demo/client/landmark-demo.js @@ -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); + } +}; diff --git a/examples/landmark-demo/landmark-demo.css b/examples/landmark-demo/landmark-demo.css new file mode 100644 index 0000000000..b6b4052b43 --- /dev/null +++ b/examples/landmark-demo/landmark-demo.css @@ -0,0 +1 @@ +/* CSS declarations go here */ diff --git a/examples/landmark-demo/landmark-demo.html b/examples/landmark-demo/landmark-demo.html new file mode 100644 index 0000000000..2818193c41 --- /dev/null +++ b/examples/landmark-demo/landmark-demo.html @@ -0,0 +1,60 @@ + + landmark-demo + + + +

Landmark demo

+ {{> redrawButtons }} + {{> preserveDemo }} + + {{> stateDemo }} + + + + + + + + + + + diff --git a/packages/handlebars/evaluate.js b/packages/handlebars/evaluate.js index e1ccb2fc5e..cd202183c4 100644 --- a/packages/handlebars/evaluate.js +++ b/packages/handlebars/evaluate.js @@ -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); diff --git a/packages/templating/deftemplate.js b/packages/templating/deftemplate.js index 3f998f8d6f..7e52959e12 100644 --- a/packages/templating/deftemplate.js +++ b/packages/templating/deftemplate.js @@ -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;