-
Notifications
You must be signed in to change notification settings - Fork 0
/
deftemplate.js
207 lines (184 loc) · 6.62 KB
/
deftemplate.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
Template = {};
var registeredPartials = {};
// XXX Handlebars hooking is janky and gross
var hookHandlebars = function () {
hookHandlebars = function(){}; // install the hook only once
var orig = Handlebars._default_helpers.each;
Handlebars._default_helpers.each = function (arg, options) {
// if arg isn't an observable (like LocalCollection.Cursor),
// don't use this reactive implementation of #each.
if (!(arg && 'observeChanges' in arg))
return orig.call(this, arg, options);
return Spark.list(
arg,
function (item) {
return Spark.labelBranch(
(item && item._id) || Spark.UNIQUE_LABEL, function () {
var html = Spark.isolate(_.bind(options.fn, null, item));
return Spark.setDataContext(item, html);
});
},
function () {
return options.inverse ?
Spark.isolate(options.inverse) : '';
}
);
};
_.extend(Handlebars._default_helpers, {
isolate: function (options) {
var data = this;
return Spark.isolate(function () {
return options.fn(data);
});
},
constant: function (options) {
var data = this;
return Spark.createLandmark({ constant: true }, function () {
return options.fn(data);
});
}
});
};
// map from landmark id, to the 'this' object for
// created/rendered/destroyed callbacks on templates
var templateInstanceData = {};
var templateObjFromLandmark = function (landmark) {
var template = templateInstanceData[landmark.id] || (
templateInstanceData[landmark.id] = {
// set these once
find: function (selector) {
if (! landmark.hasDom())
throw new Error("Template not in DOM");
return landmark.find(selector);
},
findAll: function (selector) {
if (! landmark.hasDom())
throw new Error("Template not in DOM");
return landmark.findAll(selector);
}
});
// set these each time
template.firstNode = landmark.hasDom() ? landmark.firstNode() : null;
template.lastNode = landmark.hasDom() ? landmark.lastNode() : null;
return template;
};
// XXX forms hooks into this to add "bind"?
var templateBase = {
// methods store data here (event map, etc.). initialized per template.
_tmpl_data: null,
// these functions must be generic (i.e. use `this`)
events: function (eventMap) {
var events =
(this._tmpl_data.events = (this._tmpl_data.events || {}));
_.each(eventMap, function(callback, spec) {
events[spec] = (events[spec] || []);
events[spec].push(callback);
});
},
preserve: function (preserveMap) {
var preserve =
(this._tmpl_data.preserve = (this._tmpl_data.preserve || {}));
if (_.isArray(preserveMap))
_.each(preserveMap, function (selector) {
preserve[selector] = true;
});
else
_.extend(preserve, preserveMap);
},
helpers: function (helperMap) {
var helpers =
(this._tmpl_data.helpers = (this._tmpl_data.helpers || {}));
for(var h in helperMap)
helpers[h] = helperMap[h];
}
};
Template.__define__ = function (name, raw_func) {
hookHandlebars();
if (name === '__define__')
throw new Error("Sorry, '__define__' is a special name and " +
"cannot be used as the name of a template");
// Define the function assigned to Template.<name>.
var partial = function (data) {
var tmpl = name && Template[name] || {};
var tmplData = tmpl._tmpl_data || {};
var html = Spark.labelBranch("Template."+name, function () {
var html = Spark.createLandmark({
preserve: tmplData.preserve || {},
created: function () {
var template = templateObjFromLandmark(this);
template.data = data;
tmpl.created && tmpl.created.call(template);
},
rendered: function () {
var template = templateObjFromLandmark(this);
template.data = data;
tmpl.rendered && tmpl.rendered.call(template);
},
destroyed: function () {
// template.data is already set from previous callbacks
tmpl.destroyed &&
tmpl.destroyed.call(templateObjFromLandmark(this));
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: _.extend({}, partial, tmplData.helpers || {}),
partials: registeredPartials,
name: name
});
});
// take an event map with `function (event, template)` handlers
// and produce one with `function (event, landmark)` handlers
// for Spark, by inserting logic to create the template object.
var wrapEventMap = function (oldEventMap) {
var newEventMap = {};
_.each(oldEventMap, function (handlers, key) {
if ('function' === typeof handlers) {
//Template.foo.events = ... way will give a fn, not an array
handlers = [ handlers ];
}
newEventMap[key] = _.map(handlers, function (handler) {
return function (event, landmark) {
return handler.call(this, event,
templateObjFromLandmark(landmark));
};
});
});
return newEventMap;
};
// support old Template.foo.events = {...} format
var events =
(tmpl.events !== templateBase.events ?
tmpl.events : tmplData.events);
// 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(wrapEventMap(events), html);
return html;
});
html = Spark.setDataContext(data, html);
return html;
});
return html;
};
// XXX hack.. copy all of Handlebars' built in helpers over to
// the partial. it would be better to hook helperMissing (or
// something like that?) so that Template.foo is searched only
// if it's not a built-in helper.
_.extend(partial, Handlebars.helpers);
if (name) {
if (Template[name])
throw new Error("There are multiple templates named '" + name +
"'. Each template needs a unique name.");
Template[name] = partial;
_.extend(partial, templateBase);
partial._tmpl_data = {};
registeredPartials[name] = partial;
}
// useful for unnamed templates, like body
return partial;
};