/
templates.js
154 lines (128 loc) · 4.44 KB
/
templates.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
/*
* Geddy JavaScript Web development framework
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var util = require('util');
var fs = require('fs');
var path = require('path');
var fleegix = require('./fleegix');
var async = require('geddy-util/lib/async');
var templates = {};
templates.TemplateNode = function (id, url, params, parentNode) {
this.id = id;
this.url = url;
this.dirname = path.dirname(url);
this.params = params;
this.parentNode = parentNode || null;
this.loaded = false;
this.cachedFinish = false;
this.childNodes = {};
this.content = null;
};
templates.TemplateNode.prototype = new function () {
// Only the root template node gets this -- it's the function that
// passes the completed content back to be written out in the response
this.finishRoot = null;
this.childFinished = function (childNode) {
this.content = this.content.replace(
'###partial###' + childNode.id, childNode.content);
// Every time a child node finished, this node may actually
// be finished
this.tryFinish();
};
this.tryFinish = function () {
// If this node is actually finished, either call up the
// chain, or finish the entire thing out
if (this.isFinished()) {
if (this.parentNode) {
this.parentNode.childFinished(this);
}
else {
this.finishRoot();
}
}
};
var _createLoader = function (node) {
return function () { node.loadTemplate.call(node); };
};
this.loadChildTemplates = function () {
var childNodes = this.childNodes;
var childNode;
for (var p in childNodes) {
childNode = childNodes[p];
async.execNonBlocking(_createLoader(childNode));
}
};
this.isFinished = function () {
// May be a recursive check down into a child -- if the child
// is not loaded, it's not finished
if (!this.loaded) {
return false;
}
// If we've already closed this node out, use the cached finish-flag
if (this.cachedFinish) {
return true;
}
// If any of the child nodes of this parent haven't loaded yet,
// bail out -- this will get re-called every time one of them
// comes in, until they're all there and we can move on up the chain
var childNodes = this.childNodes;
for (var p in childNodes) {
if (!childNodes[p].isFinished()) {
return false;
}
}
// Once this guy is finished cache the results to avoid
// a bunch of recursive calls when it's called as a child
this.cachedFinish = true;
return true;
};
this.loadTemplate = function () {
var _this = this;
var handleLoaded = function (data) {
_this.loaded = true;
// Create a template out of the markup in the file
var templ = new fleegix.ejs.Template({text: data});
// Execute the template with the content as the data-object
// This may execute "partial" method calls that create child
// nodes for this one.
templ.process({data: _this.params});
_this.content = templ.markup;
// By the time we get here, all 'partial' method calls will
// have executed, and any children will have been added
_this.loadChildTemplates();
// This node is loaded, and we now know if it has any children
// -- it may be finished, too.
_this.tryFinish();
};
var cached = templates.textCache[this.url];
// Use cached template text if possible
if (geddy.config.environment == 'production' && cached) {
handleLoaded(cached);
}
// If this is the first hit, use the template text off disc
// and cache it for subsequent requests
else {
fs.readFile(this.url, 'utf8', function (err, data) {
if (err) { throw err; }
templates.textCache[_this.url] = data;
handleLoaded(data);
});
}
};
}();
templates.textCache = {};
exports.TemplateNode = templates.TemplateNode;