-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtemplate.js
188 lines (144 loc) · 5.11 KB
/
template.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
;(function () {
var __templates = {};
var template = function (name, data) {
if (!(name in __templates)) {
Lens.log.debug('template {%s} not found in the cache; compiling from source.', name);
__templates[name] = compile(name);
}
return __templates[name](data || {});
};
var parse = function (src) {
var tokenizer = new RegExp('([\\s\\S]*?)\\[\\[([\\s\\S]*?)\\]\\]([\\s\\S]*)');
var str = function (s) {
if (!s) { return "''"; }
return "'"+s.replace(/(['\\])/g, '\\$1').replace(/\n/g, "\\n")+"'";
};
var code = [];
for (;;) {
var tokens = tokenizer.exec(src)
if (!tokens) {
code.push('__ += '+str(src)+';');
break;
}
if (tokens[2][0] == ':') { /* trim preceeding literal */
tokens[1] = tokens[1].replace(/\s+$/, '');
tokens[2] = tokens[2].substr(1);
}
if (tokens[2][tokens[2].length - 1] == ':') { /* trim following literal */
tokens[3] = tokens[3].replace(/^\s+/, '');
tokens[2] = tokens[2].substr(0, tokens[2].length-2);
}
code.push('__ += '+str(tokens[1])+';');
if (tokens[2][0] == '=') {
code.push('__ += ('+tokens[2].replace(/^=\s*/, '')+');');
} else if (tokens[2][0] != '#') { /* skip comments */
code.push(tokens[2]);
}
src = tokens[3];
}
return code.join('');
};
var compile = function (name) {
name = name.toString();
var script = document.getElementById('template:'+name);
if (!script) {
Lens.log.error('unable to find a <script> element with id="template:%s"', name);
return function () {
throw "Template {"+name+"} not found";
};
}
var code = parse(script.innerHTML);
return function (_) {
/* the output variable */
var __ = '';
/* namespaced helper functions */
var lens = {
/* maybe(x,fallback)
fallback to a default value if a given variable
is undefined, or was not provided.
example:
[[= lens.maybe(x, "no x given") ]]
*/
maybe: function (a, b) {
return typeof(a) !== 'undefined' ? a : b;
},
/* escapeHTML(x)
return a sanitized version of x, with the dangerous HTML
entities like <, > and & replaced. Also replaces double
quote (") with the " representation, so that you can
embed values in form element attributes.
example:
<input type="text" name="display"
value="[[= lens.htmlEscape(_.display) ]]">
lens.h() is an alias, so you can also do this:
<input type="text" name="display"
value="[[= lens.h(_.display) ]]">
*/
escapeHTML: function (s) {
var t = document.createElement('textarea');
t.innerText = s;
return t.innerHTML.replace(/"/g, '"');
},
/* include(template)
include(template, _.other.data)
splices the output of another template into the current
output, at the calling site. This can be useful for
breaking up common elements of a UI into more manageable
chunks.
example:
<div id="login">[[ lens.include('signin'); ]]</div>
you can also provide a data object that will become the
`_` variable inside the called template:
[[ lens.include('alert', { alert: "something broke" }); ]]
as a "language construct", this function is also aliased
as (toplevel) `include()`, and it can be used in [[= ]]
constructs:
[[= include('other-template') ]]
*/
include: function (name, data) {
__ += template(name, data || _);
return '';
}
};
/* aliases ... */
lens.u = encodeURIComponent;
lens.h = lens.escapeHTML;
var include = lens.include;
Lens.log.debug('evaluating the {%s} template', name);
eval(code);
return __;
};
};
window.parseTemplate = parse;
if (typeof(jQuery) !== 'undefined') {
jQuery.template = template;
jQuery.fn.template = function (name, data, force) {
if (force || this.length == 0) {
this.html(template(name, data)).data('lens:template', {
template: name,
data: data
});
return this;
}
var was = this.data('lens:template');
if (was && (!name || was.template == name)) {
data = typeof(data) === 'undefined' ? was.data : data;
window.patch(this[0], diff(this[0], $('<div>'+template(was.template, data)+'</div>')[0]));
this.data('lens:template', {
template: was.template,
data: data
});
return this;
}
this.html(template(name, data)).data('lens:template', {
template: name,
data: data
});
return this;
};
} else if (typeof(window) !== 'undefined') {
window.template = template;
} else {
throw 'neither jQuery or top-level window object were found; unsure where to attach template()...';
}
})();