root / snf-astakos-app / astakos / im / static / im / js / mustache.js @ 36dacb92
History | View | Annotate | Download (14.4 kB)
1 | 4e03ba30 | Kostas Papadimitriou | /*!
|
---|---|---|---|
2 | 4e03ba30 | Kostas Papadimitriou | * mustache.js - Logic-less {{mustache}} templates with JavaScript
|
3 | 4e03ba30 | Kostas Papadimitriou | * http://github.com/janl/mustache.js
|
4 | 4e03ba30 | Kostas Papadimitriou | */
|
5 | 4e03ba30 | Kostas Papadimitriou | |
6 | 4e03ba30 | Kostas Papadimitriou | /*global define: false*/
|
7 | 4e03ba30 | Kostas Papadimitriou | |
8 | 4e03ba30 | Kostas Papadimitriou | (function (root, mustache) {
|
9 | 4e03ba30 | Kostas Papadimitriou | if (typeof exports === "object" && exports) { |
10 | 4e03ba30 | Kostas Papadimitriou | module.exports = mustache; // CommonJS
|
11 | 4e03ba30 | Kostas Papadimitriou | } else if (typeof define === "function" && define.amd) { |
12 | 4e03ba30 | Kostas Papadimitriou | define(mustache); // AMD
|
13 | 4e03ba30 | Kostas Papadimitriou | } else {
|
14 | 4e03ba30 | Kostas Papadimitriou | root.Mustache = mustache; // <script>
|
15 | 4e03ba30 | Kostas Papadimitriou | } |
16 | 4e03ba30 | Kostas Papadimitriou | }(this, (function () { |
17 | 4e03ba30 | Kostas Papadimitriou | |
18 | 4e03ba30 | Kostas Papadimitriou | var whiteRe = /\s*/; |
19 | 4e03ba30 | Kostas Papadimitriou | var spaceRe = /\s+/; |
20 | 4e03ba30 | Kostas Papadimitriou | var nonSpaceRe = /\S/; |
21 | 4e03ba30 | Kostas Papadimitriou | var eqRe = /\s*=/; |
22 | 4e03ba30 | Kostas Papadimitriou | var curlyRe = /\s*\}/; |
23 | 4e03ba30 | Kostas Papadimitriou | var tagRe = /#|\^|\/|>|\{|&|=|!/; |
24 | 4e03ba30 | Kostas Papadimitriou | |
25 | 4e03ba30 | Kostas Papadimitriou | var _test = RegExp.prototype.test;
|
26 | 4e03ba30 | Kostas Papadimitriou | var _toString = Object.prototype.toString;
|
27 | 4e03ba30 | Kostas Papadimitriou | |
28 | 4e03ba30 | Kostas Papadimitriou | // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
|
29 | 4e03ba30 | Kostas Papadimitriou | // See https://github.com/janl/mustache.js/issues/189
|
30 | 4e03ba30 | Kostas Papadimitriou | function testRe(re, string) { |
31 | 4e03ba30 | Kostas Papadimitriou | return _test.call(re, string);
|
32 | 4e03ba30 | Kostas Papadimitriou | } |
33 | 4e03ba30 | Kostas Papadimitriou | |
34 | 4e03ba30 | Kostas Papadimitriou | function isWhitespace(string) { |
35 | 4e03ba30 | Kostas Papadimitriou | return !testRe(nonSpaceRe, string);
|
36 | 4e03ba30 | Kostas Papadimitriou | } |
37 | 4e03ba30 | Kostas Papadimitriou | |
38 | 4e03ba30 | Kostas Papadimitriou | var isArray = Array.isArray || function (obj) { |
39 | 4e03ba30 | Kostas Papadimitriou | return _toString.call(obj) === '[object Array]'; |
40 | 4e03ba30 | Kostas Papadimitriou | }; |
41 | 4e03ba30 | Kostas Papadimitriou | |
42 | 4e03ba30 | Kostas Papadimitriou | function escapeRe(string) { |
43 | 4e03ba30 | Kostas Papadimitriou | return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); |
44 | 4e03ba30 | Kostas Papadimitriou | } |
45 | 4e03ba30 | Kostas Papadimitriou | |
46 | 4e03ba30 | Kostas Papadimitriou | var entityMap = {
|
47 | 4e03ba30 | Kostas Papadimitriou | "&": "&", |
48 | 4e03ba30 | Kostas Papadimitriou | "<": "<", |
49 | 4e03ba30 | Kostas Papadimitriou | ">": ">", |
50 | 4e03ba30 | Kostas Papadimitriou | '"': '"', |
51 | 4e03ba30 | Kostas Papadimitriou | "'": ''', |
52 | 4e03ba30 | Kostas Papadimitriou | "/": '/' |
53 | 4e03ba30 | Kostas Papadimitriou | }; |
54 | 4e03ba30 | Kostas Papadimitriou | |
55 | 4e03ba30 | Kostas Papadimitriou | function escapeHtml(string) { |
56 | 4e03ba30 | Kostas Papadimitriou | return String(string).replace(/[&<>"'\/]/g, function (s) { |
57 | 4e03ba30 | Kostas Papadimitriou | return entityMap[s];
|
58 | 4e03ba30 | Kostas Papadimitriou | }); |
59 | 4e03ba30 | Kostas Papadimitriou | } |
60 | 4e03ba30 | Kostas Papadimitriou | |
61 | 4e03ba30 | Kostas Papadimitriou | function Scanner(string) { |
62 | 4e03ba30 | Kostas Papadimitriou | this.string = string;
|
63 | 4e03ba30 | Kostas Papadimitriou | this.tail = string;
|
64 | 4e03ba30 | Kostas Papadimitriou | this.pos = 0; |
65 | 4e03ba30 | Kostas Papadimitriou | } |
66 | 4e03ba30 | Kostas Papadimitriou | |
67 | 4e03ba30 | Kostas Papadimitriou | /**
|
68 | 4e03ba30 | Kostas Papadimitriou | * Returns `true` if the tail is empty (end of string).
|
69 | 4e03ba30 | Kostas Papadimitriou | */
|
70 | 4e03ba30 | Kostas Papadimitriou | Scanner.prototype.eos = function () { |
71 | 4e03ba30 | Kostas Papadimitriou | return this.tail === ""; |
72 | 4e03ba30 | Kostas Papadimitriou | }; |
73 | 4e03ba30 | Kostas Papadimitriou | |
74 | 4e03ba30 | Kostas Papadimitriou | /**
|
75 | 4e03ba30 | Kostas Papadimitriou | * Tries to match the given regular expression at the current position.
|
76 | 4e03ba30 | Kostas Papadimitriou | * Returns the matched text if it can match, the empty string otherwise.
|
77 | 4e03ba30 | Kostas Papadimitriou | */
|
78 | 4e03ba30 | Kostas Papadimitriou | Scanner.prototype.scan = function (re) { |
79 | 4e03ba30 | Kostas Papadimitriou | var match = this.tail.match(re); |
80 | 4e03ba30 | Kostas Papadimitriou | |
81 | 4e03ba30 | Kostas Papadimitriou | if (match && match.index === 0) { |
82 | 4e03ba30 | Kostas Papadimitriou | this.tail = this.tail.substring(match[0].length); |
83 | 4e03ba30 | Kostas Papadimitriou | this.pos += match[0].length; |
84 | 4e03ba30 | Kostas Papadimitriou | return match[0]; |
85 | 4e03ba30 | Kostas Papadimitriou | } |
86 | 4e03ba30 | Kostas Papadimitriou | |
87 | 4e03ba30 | Kostas Papadimitriou | return ""; |
88 | 4e03ba30 | Kostas Papadimitriou | }; |
89 | 4e03ba30 | Kostas Papadimitriou | |
90 | 4e03ba30 | Kostas Papadimitriou | /**
|
91 | 4e03ba30 | Kostas Papadimitriou | * Skips all text until the given regular expression can be matched. Returns
|
92 | 4e03ba30 | Kostas Papadimitriou | * the skipped string, which is the entire tail if no match can be made.
|
93 | 4e03ba30 | Kostas Papadimitriou | */
|
94 | 4e03ba30 | Kostas Papadimitriou | Scanner.prototype.scanUntil = function (re) { |
95 | 4e03ba30 | Kostas Papadimitriou | var match, pos = this.tail.search(re); |
96 | 4e03ba30 | Kostas Papadimitriou | |
97 | 4e03ba30 | Kostas Papadimitriou | switch (pos) {
|
98 | 4e03ba30 | Kostas Papadimitriou | case -1: |
99 | 4e03ba30 | Kostas Papadimitriou | match = this.tail;
|
100 | 4e03ba30 | Kostas Papadimitriou | this.pos += this.tail.length; |
101 | 4e03ba30 | Kostas Papadimitriou | this.tail = ""; |
102 | 4e03ba30 | Kostas Papadimitriou | break;
|
103 | 4e03ba30 | Kostas Papadimitriou | case 0: |
104 | 4e03ba30 | Kostas Papadimitriou | match = "";
|
105 | 4e03ba30 | Kostas Papadimitriou | break;
|
106 | 4e03ba30 | Kostas Papadimitriou | default:
|
107 | 4e03ba30 | Kostas Papadimitriou | match = this.tail.substring(0, pos); |
108 | 4e03ba30 | Kostas Papadimitriou | this.tail = this.tail.substring(pos); |
109 | 4e03ba30 | Kostas Papadimitriou | this.pos += pos;
|
110 | 4e03ba30 | Kostas Papadimitriou | } |
111 | 4e03ba30 | Kostas Papadimitriou | |
112 | 4e03ba30 | Kostas Papadimitriou | return match;
|
113 | 4e03ba30 | Kostas Papadimitriou | }; |
114 | 4e03ba30 | Kostas Papadimitriou | |
115 | 4e03ba30 | Kostas Papadimitriou | function Context(view, parent) { |
116 | 4e03ba30 | Kostas Papadimitriou | this.view = view || {};
|
117 | 4e03ba30 | Kostas Papadimitriou | this.parent = parent;
|
118 | 4e03ba30 | Kostas Papadimitriou | this._cache = {};
|
119 | 4e03ba30 | Kostas Papadimitriou | } |
120 | 4e03ba30 | Kostas Papadimitriou | |
121 | 4e03ba30 | Kostas Papadimitriou | Context.make = function (view) { |
122 | 4e03ba30 | Kostas Papadimitriou | return (view instanceof Context) ? view : new Context(view); |
123 | 4e03ba30 | Kostas Papadimitriou | }; |
124 | 4e03ba30 | Kostas Papadimitriou | |
125 | 4e03ba30 | Kostas Papadimitriou | Context.prototype.push = function (view) { |
126 | 4e03ba30 | Kostas Papadimitriou | return new Context(view, this); |
127 | 4e03ba30 | Kostas Papadimitriou | }; |
128 | 4e03ba30 | Kostas Papadimitriou | |
129 | 4e03ba30 | Kostas Papadimitriou | Context.prototype.lookup = function (name) { |
130 | 4e03ba30 | Kostas Papadimitriou | var value = this._cache[name]; |
131 | 4e03ba30 | Kostas Papadimitriou | |
132 | 4e03ba30 | Kostas Papadimitriou | if (!value) {
|
133 | 4e03ba30 | Kostas Papadimitriou | if (name == '.') { |
134 | 4e03ba30 | Kostas Papadimitriou | value = this.view;
|
135 | 4e03ba30 | Kostas Papadimitriou | } else {
|
136 | 4e03ba30 | Kostas Papadimitriou | var context = this; |
137 | 4e03ba30 | Kostas Papadimitriou | |
138 | 4e03ba30 | Kostas Papadimitriou | while (context) {
|
139 | 4e03ba30 | Kostas Papadimitriou | if (name.indexOf('.') > 0) { |
140 | 4e03ba30 | Kostas Papadimitriou | value = context.view; |
141 | 4e03ba30 | Kostas Papadimitriou | var names = name.split('.'), i = 0; |
142 | 4e03ba30 | Kostas Papadimitriou | while (value && i < names.length) {
|
143 | 4e03ba30 | Kostas Papadimitriou | value = value[names[i++]]; |
144 | 4e03ba30 | Kostas Papadimitriou | } |
145 | 4e03ba30 | Kostas Papadimitriou | } else {
|
146 | 4e03ba30 | Kostas Papadimitriou | value = context.view[name]; |
147 | 4e03ba30 | Kostas Papadimitriou | } |
148 | 4e03ba30 | Kostas Papadimitriou | |
149 | 4e03ba30 | Kostas Papadimitriou | if (value != null) break; |
150 | 4e03ba30 | Kostas Papadimitriou | |
151 | 4e03ba30 | Kostas Papadimitriou | context = context.parent; |
152 | 4e03ba30 | Kostas Papadimitriou | } |
153 | 4e03ba30 | Kostas Papadimitriou | } |
154 | 4e03ba30 | Kostas Papadimitriou | |
155 | 4e03ba30 | Kostas Papadimitriou | this._cache[name] = value;
|
156 | 4e03ba30 | Kostas Papadimitriou | } |
157 | 4e03ba30 | Kostas Papadimitriou | |
158 | 4e03ba30 | Kostas Papadimitriou | if (typeof value === 'function') value = value.call(this.view); |
159 | 4e03ba30 | Kostas Papadimitriou | |
160 | 4e03ba30 | Kostas Papadimitriou | return value;
|
161 | 4e03ba30 | Kostas Papadimitriou | }; |
162 | 4e03ba30 | Kostas Papadimitriou | |
163 | 4e03ba30 | Kostas Papadimitriou | function Writer() { |
164 | 4e03ba30 | Kostas Papadimitriou | this.clearCache();
|
165 | 4e03ba30 | Kostas Papadimitriou | } |
166 | 4e03ba30 | Kostas Papadimitriou | |
167 | 4e03ba30 | Kostas Papadimitriou | Writer.prototype.clearCache = function () { |
168 | 4e03ba30 | Kostas Papadimitriou | this._cache = {};
|
169 | 4e03ba30 | Kostas Papadimitriou | this._partialCache = {};
|
170 | 4e03ba30 | Kostas Papadimitriou | }; |
171 | 4e03ba30 | Kostas Papadimitriou | |
172 | 4e03ba30 | Kostas Papadimitriou | Writer.prototype.compile = function (template, tags) { |
173 | 4e03ba30 | Kostas Papadimitriou | var fn = this._cache[template]; |
174 | 4e03ba30 | Kostas Papadimitriou | |
175 | 4e03ba30 | Kostas Papadimitriou | if (!fn) {
|
176 | 4e03ba30 | Kostas Papadimitriou | var tokens = mustache.parse(template, tags);
|
177 | 4e03ba30 | Kostas Papadimitriou | fn = this._cache[template] = this.compileTokens(tokens, template); |
178 | 4e03ba30 | Kostas Papadimitriou | } |
179 | 4e03ba30 | Kostas Papadimitriou | |
180 | 4e03ba30 | Kostas Papadimitriou | return fn;
|
181 | 4e03ba30 | Kostas Papadimitriou | }; |
182 | 4e03ba30 | Kostas Papadimitriou | |
183 | 4e03ba30 | Kostas Papadimitriou | Writer.prototype.compilePartial = function (name, template, tags) { |
184 | 4e03ba30 | Kostas Papadimitriou | var fn = this.compile(template, tags); |
185 | 4e03ba30 | Kostas Papadimitriou | this._partialCache[name] = fn;
|
186 | 4e03ba30 | Kostas Papadimitriou | return fn;
|
187 | 4e03ba30 | Kostas Papadimitriou | }; |
188 | 4e03ba30 | Kostas Papadimitriou | |
189 | 4e03ba30 | Kostas Papadimitriou | Writer.prototype.getPartial = function (name) { |
190 | 4e03ba30 | Kostas Papadimitriou | if (!(name in this._partialCache) && this._loadPartial) { |
191 | 4e03ba30 | Kostas Papadimitriou | this.compilePartial(name, this._loadPartial(name)); |
192 | 4e03ba30 | Kostas Papadimitriou | } |
193 | 4e03ba30 | Kostas Papadimitriou | |
194 | 4e03ba30 | Kostas Papadimitriou | return this._partialCache[name]; |
195 | 4e03ba30 | Kostas Papadimitriou | }; |
196 | 4e03ba30 | Kostas Papadimitriou | |
197 | 4e03ba30 | Kostas Papadimitriou | Writer.prototype.compileTokens = function (tokens, template) { |
198 | 4e03ba30 | Kostas Papadimitriou | var self = this; |
199 | 4e03ba30 | Kostas Papadimitriou | return function (view, partials) { |
200 | 4e03ba30 | Kostas Papadimitriou | if (partials) {
|
201 | 4e03ba30 | Kostas Papadimitriou | if (typeof partials === 'function') { |
202 | 4e03ba30 | Kostas Papadimitriou | self._loadPartial = partials; |
203 | 4e03ba30 | Kostas Papadimitriou | } else {
|
204 | 4e03ba30 | Kostas Papadimitriou | for (var name in partials) { |
205 | 4e03ba30 | Kostas Papadimitriou | self.compilePartial(name, partials[name]); |
206 | 4e03ba30 | Kostas Papadimitriou | } |
207 | 4e03ba30 | Kostas Papadimitriou | } |
208 | 4e03ba30 | Kostas Papadimitriou | } |
209 | 4e03ba30 | Kostas Papadimitriou | |
210 | 4e03ba30 | Kostas Papadimitriou | return renderTokens(tokens, self, Context.make(view), template);
|
211 | 4e03ba30 | Kostas Papadimitriou | }; |
212 | 4e03ba30 | Kostas Papadimitriou | }; |
213 | 4e03ba30 | Kostas Papadimitriou | |
214 | 4e03ba30 | Kostas Papadimitriou | Writer.prototype.render = function (template, view, partials) { |
215 | 4e03ba30 | Kostas Papadimitriou | return this.compile(template)(view, partials); |
216 | 4e03ba30 | Kostas Papadimitriou | }; |
217 | 4e03ba30 | Kostas Papadimitriou | |
218 | 4e03ba30 | Kostas Papadimitriou | /**
|
219 | 4e03ba30 | Kostas Papadimitriou | * Low-level function that renders the given `tokens` using the given `writer`
|
220 | 4e03ba30 | Kostas Papadimitriou | * and `context`. The `template` string is only needed for templates that use
|
221 | 4e03ba30 | Kostas Papadimitriou | * higher-order sections to extract the portion of the original template that
|
222 | 4e03ba30 | Kostas Papadimitriou | * was contained in that section.
|
223 | 4e03ba30 | Kostas Papadimitriou | */
|
224 | 4e03ba30 | Kostas Papadimitriou | function renderTokens(tokens, writer, context, template) { |
225 | 4e03ba30 | Kostas Papadimitriou | var buffer = ''; |
226 | 4e03ba30 | Kostas Papadimitriou | |
227 | 4e03ba30 | Kostas Papadimitriou | var token, tokenValue, value;
|
228 | 4e03ba30 | Kostas Papadimitriou | for (var i = 0, len = tokens.length; i < len; ++i) { |
229 | 4e03ba30 | Kostas Papadimitriou | token = tokens[i]; |
230 | 4e03ba30 | Kostas Papadimitriou | tokenValue = token[1];
|
231 | 4e03ba30 | Kostas Papadimitriou | |
232 | 4e03ba30 | Kostas Papadimitriou | switch (token[0]) { |
233 | 4e03ba30 | Kostas Papadimitriou | case '#': |
234 | 4e03ba30 | Kostas Papadimitriou | value = context.lookup(tokenValue); |
235 | 4e03ba30 | Kostas Papadimitriou | |
236 | 4e03ba30 | Kostas Papadimitriou | if (typeof value === 'object') { |
237 | 4e03ba30 | Kostas Papadimitriou | if (isArray(value)) {
|
238 | 4e03ba30 | Kostas Papadimitriou | for (var j = 0, jlen = value.length; j < jlen; ++j) { |
239 | 4e03ba30 | Kostas Papadimitriou | buffer += renderTokens(token[4], writer, context.push(value[j]), template);
|
240 | 4e03ba30 | Kostas Papadimitriou | } |
241 | 4e03ba30 | Kostas Papadimitriou | } else if (value) { |
242 | 4e03ba30 | Kostas Papadimitriou | buffer += renderTokens(token[4], writer, context.push(value), template);
|
243 | 4e03ba30 | Kostas Papadimitriou | } |
244 | 4e03ba30 | Kostas Papadimitriou | } else if (typeof value === 'function') { |
245 | 4e03ba30 | Kostas Papadimitriou | var text = template == null ? null : template.slice(token[3], token[5]); |
246 | 4e03ba30 | Kostas Papadimitriou | value = value.call(context.view, text, function (template) {
|
247 | 4e03ba30 | Kostas Papadimitriou | return writer.render(template, context);
|
248 | 4e03ba30 | Kostas Papadimitriou | }); |
249 | 4e03ba30 | Kostas Papadimitriou | if (value != null) buffer += value; |
250 | 4e03ba30 | Kostas Papadimitriou | } else if (value) { |
251 | 4e03ba30 | Kostas Papadimitriou | buffer += renderTokens(token[4], writer, context, template);
|
252 | 4e03ba30 | Kostas Papadimitriou | } |
253 | 4e03ba30 | Kostas Papadimitriou | |
254 | 4e03ba30 | Kostas Papadimitriou | break;
|
255 | 4e03ba30 | Kostas Papadimitriou | case '^': |
256 | 4e03ba30 | Kostas Papadimitriou | value = context.lookup(tokenValue); |
257 | 4e03ba30 | Kostas Papadimitriou | |
258 | 4e03ba30 | Kostas Papadimitriou | // Use JavaScript's definition of falsy. Include empty arrays.
|
259 | 4e03ba30 | Kostas Papadimitriou | // See https://github.com/janl/mustache.js/issues/186
|
260 | 4e03ba30 | Kostas Papadimitriou | if (!value || (isArray(value) && value.length === 0)) { |
261 | 4e03ba30 | Kostas Papadimitriou | buffer += renderTokens(token[4], writer, context, template);
|
262 | 4e03ba30 | Kostas Papadimitriou | } |
263 | 4e03ba30 | Kostas Papadimitriou | |
264 | 4e03ba30 | Kostas Papadimitriou | break;
|
265 | 4e03ba30 | Kostas Papadimitriou | case '>': |
266 | 4e03ba30 | Kostas Papadimitriou | value = writer.getPartial(tokenValue); |
267 | 4e03ba30 | Kostas Papadimitriou | if (typeof value === 'function') buffer += value(context); |
268 | 4e03ba30 | Kostas Papadimitriou | break;
|
269 | 4e03ba30 | Kostas Papadimitriou | case '&': |
270 | 4e03ba30 | Kostas Papadimitriou | value = context.lookup(tokenValue); |
271 | 4e03ba30 | Kostas Papadimitriou | if (value != null) buffer += value; |
272 | 4e03ba30 | Kostas Papadimitriou | break;
|
273 | 4e03ba30 | Kostas Papadimitriou | case 'name': |
274 | 4e03ba30 | Kostas Papadimitriou | value = context.lookup(tokenValue); |
275 | 4e03ba30 | Kostas Papadimitriou | if (value != null) buffer += mustache.escape(value); |
276 | 4e03ba30 | Kostas Papadimitriou | break;
|
277 | 4e03ba30 | Kostas Papadimitriou | case 'text': |
278 | 4e03ba30 | Kostas Papadimitriou | buffer += tokenValue; |
279 | 4e03ba30 | Kostas Papadimitriou | break;
|
280 | 4e03ba30 | Kostas Papadimitriou | } |
281 | 4e03ba30 | Kostas Papadimitriou | } |
282 | 4e03ba30 | Kostas Papadimitriou | |
283 | 4e03ba30 | Kostas Papadimitriou | return buffer;
|
284 | 4e03ba30 | Kostas Papadimitriou | } |
285 | 4e03ba30 | Kostas Papadimitriou | |
286 | 4e03ba30 | Kostas Papadimitriou | /**
|
287 | 4e03ba30 | Kostas Papadimitriou | * Forms the given array of `tokens` into a nested tree structure where
|
288 | 4e03ba30 | Kostas Papadimitriou | * tokens that represent a section have two additional items: 1) an array of
|
289 | 4e03ba30 | Kostas Papadimitriou | * all tokens that appear in that section and 2) the index in the original
|
290 | 4e03ba30 | Kostas Papadimitriou | * template that represents the end of that section.
|
291 | 4e03ba30 | Kostas Papadimitriou | */
|
292 | 4e03ba30 | Kostas Papadimitriou | function nestTokens(tokens) { |
293 | 4e03ba30 | Kostas Papadimitriou | var tree = [];
|
294 | 4e03ba30 | Kostas Papadimitriou | var collector = tree;
|
295 | 4e03ba30 | Kostas Papadimitriou | var sections = [];
|
296 | 4e03ba30 | Kostas Papadimitriou | |
297 | 4e03ba30 | Kostas Papadimitriou | var token;
|
298 | 4e03ba30 | Kostas Papadimitriou | for (var i = 0, len = tokens.length; i < len; ++i) { |
299 | 4e03ba30 | Kostas Papadimitriou | token = tokens[i]; |
300 | 4e03ba30 | Kostas Papadimitriou | switch (token[0]) { |
301 | 4e03ba30 | Kostas Papadimitriou | case '#': |
302 | 4e03ba30 | Kostas Papadimitriou | case '^': |
303 | 4e03ba30 | Kostas Papadimitriou | sections.push(token); |
304 | 4e03ba30 | Kostas Papadimitriou | collector.push(token); |
305 | 4e03ba30 | Kostas Papadimitriou | collector = token[4] = [];
|
306 | 4e03ba30 | Kostas Papadimitriou | break;
|
307 | 4e03ba30 | Kostas Papadimitriou | case '/': |
308 | 4e03ba30 | Kostas Papadimitriou | var section = sections.pop();
|
309 | 4e03ba30 | Kostas Papadimitriou | section[5] = token[2]; |
310 | 4e03ba30 | Kostas Papadimitriou | collector = sections.length > 0 ? sections[sections.length - 1][4] : tree; |
311 | 4e03ba30 | Kostas Papadimitriou | break;
|
312 | 4e03ba30 | Kostas Papadimitriou | default:
|
313 | 4e03ba30 | Kostas Papadimitriou | collector.push(token); |
314 | 4e03ba30 | Kostas Papadimitriou | } |
315 | 4e03ba30 | Kostas Papadimitriou | } |
316 | 4e03ba30 | Kostas Papadimitriou | |
317 | 4e03ba30 | Kostas Papadimitriou | return tree;
|
318 | 4e03ba30 | Kostas Papadimitriou | } |
319 | 4e03ba30 | Kostas Papadimitriou | |
320 | 4e03ba30 | Kostas Papadimitriou | /**
|
321 | 4e03ba30 | Kostas Papadimitriou | * Combines the values of consecutive text tokens in the given `tokens` array
|
322 | 4e03ba30 | Kostas Papadimitriou | * to a single token.
|
323 | 4e03ba30 | Kostas Papadimitriou | */
|
324 | 4e03ba30 | Kostas Papadimitriou | function squashTokens(tokens) { |
325 | 4e03ba30 | Kostas Papadimitriou | var squashedTokens = [];
|
326 | 4e03ba30 | Kostas Papadimitriou | |
327 | 4e03ba30 | Kostas Papadimitriou | var token, lastToken;
|
328 | 4e03ba30 | Kostas Papadimitriou | for (var i = 0, len = tokens.length; i < len; ++i) { |
329 | 4e03ba30 | Kostas Papadimitriou | token = tokens[i]; |
330 | 4e03ba30 | Kostas Papadimitriou | if (token) {
|
331 | 4e03ba30 | Kostas Papadimitriou | if (token[0] === 'text' && lastToken && lastToken[0] === 'text') { |
332 | 4e03ba30 | Kostas Papadimitriou | lastToken[1] += token[1]; |
333 | 4e03ba30 | Kostas Papadimitriou | lastToken[3] = token[3]; |
334 | 4e03ba30 | Kostas Papadimitriou | } else {
|
335 | 4e03ba30 | Kostas Papadimitriou | lastToken = token; |
336 | 4e03ba30 | Kostas Papadimitriou | squashedTokens.push(token); |
337 | 4e03ba30 | Kostas Papadimitriou | } |
338 | 4e03ba30 | Kostas Papadimitriou | } |
339 | 4e03ba30 | Kostas Papadimitriou | } |
340 | 4e03ba30 | Kostas Papadimitriou | |
341 | 4e03ba30 | Kostas Papadimitriou | return squashedTokens;
|
342 | 4e03ba30 | Kostas Papadimitriou | } |
343 | 4e03ba30 | Kostas Papadimitriou | |
344 | 4e03ba30 | Kostas Papadimitriou | function escapeTags(tags) { |
345 | 4e03ba30 | Kostas Papadimitriou | return [
|
346 | 4e03ba30 | Kostas Papadimitriou | new RegExp(escapeRe(tags[0]) + "\\s*"), |
347 | 4e03ba30 | Kostas Papadimitriou | new RegExp("\\s*" + escapeRe(tags[1])) |
348 | 4e03ba30 | Kostas Papadimitriou | ]; |
349 | 4e03ba30 | Kostas Papadimitriou | } |
350 | 4e03ba30 | Kostas Papadimitriou | |
351 | 4e03ba30 | Kostas Papadimitriou | /**
|
352 | 4e03ba30 | Kostas Papadimitriou | * Breaks up the given `template` string into a tree of token objects. If
|
353 | 4e03ba30 | Kostas Papadimitriou | * `tags` is given here it must be an array with two string values: the
|
354 | 4e03ba30 | Kostas Papadimitriou | * opening and closing tags used in the template (e.g. ["<%", "%>"]). Of
|
355 | 4e03ba30 | Kostas Papadimitriou | * course, the default is to use mustaches (i.e. Mustache.tags).
|
356 | 4e03ba30 | Kostas Papadimitriou | */
|
357 | 4e03ba30 | Kostas Papadimitriou | function parseTemplate(template, tags) { |
358 | 4e03ba30 | Kostas Papadimitriou | template = template || '';
|
359 | 4e03ba30 | Kostas Papadimitriou | tags = tags || mustache.tags; |
360 | 4e03ba30 | Kostas Papadimitriou | |
361 | 4e03ba30 | Kostas Papadimitriou | if (typeof tags === 'string') tags = tags.split(spaceRe); |
362 | 4e03ba30 | Kostas Papadimitriou | if (tags.length !== 2) throw new Error('Invalid tags: ' + tags.join(', ')); |
363 | 4e03ba30 | Kostas Papadimitriou | |
364 | 4e03ba30 | Kostas Papadimitriou | var tagRes = escapeTags(tags);
|
365 | 4e03ba30 | Kostas Papadimitriou | var scanner = new Scanner(template); |
366 | 4e03ba30 | Kostas Papadimitriou | |
367 | 4e03ba30 | Kostas Papadimitriou | var sections = []; // Stack to hold section tokens |
368 | 4e03ba30 | Kostas Papadimitriou | var tokens = []; // Buffer to hold the tokens |
369 | 4e03ba30 | Kostas Papadimitriou | var spaces = []; // Indices of whitespace tokens on the current line |
370 | 4e03ba30 | Kostas Papadimitriou | var hasTag = false; // Is there a {{tag}} on the current line? |
371 | 4e03ba30 | Kostas Papadimitriou | var nonSpace = false; // Is there a non-space char on the current line? |
372 | 4e03ba30 | Kostas Papadimitriou | |
373 | 4e03ba30 | Kostas Papadimitriou | // Strips all whitespace tokens array for the current line
|
374 | 4e03ba30 | Kostas Papadimitriou | // if there was a {{#tag}} on it and otherwise only space.
|
375 | 4e03ba30 | Kostas Papadimitriou | function stripSpace() { |
376 | 4e03ba30 | Kostas Papadimitriou | if (hasTag && !nonSpace) {
|
377 | 4e03ba30 | Kostas Papadimitriou | while (spaces.length) {
|
378 | 4e03ba30 | Kostas Papadimitriou | delete tokens[spaces.pop()];
|
379 | 4e03ba30 | Kostas Papadimitriou | } |
380 | 4e03ba30 | Kostas Papadimitriou | } else {
|
381 | 4e03ba30 | Kostas Papadimitriou | spaces = []; |
382 | 4e03ba30 | Kostas Papadimitriou | } |
383 | 4e03ba30 | Kostas Papadimitriou | |
384 | 4e03ba30 | Kostas Papadimitriou | hasTag = false;
|
385 | 4e03ba30 | Kostas Papadimitriou | nonSpace = false;
|
386 | 4e03ba30 | Kostas Papadimitriou | } |
387 | 4e03ba30 | Kostas Papadimitriou | |
388 | 4e03ba30 | Kostas Papadimitriou | var start, type, value, chr, token;
|
389 | 4e03ba30 | Kostas Papadimitriou | while (!scanner.eos()) {
|
390 | 4e03ba30 | Kostas Papadimitriou | start = scanner.pos; |
391 | 4e03ba30 | Kostas Papadimitriou | |
392 | 4e03ba30 | Kostas Papadimitriou | // Match any text between tags.
|
393 | 4e03ba30 | Kostas Papadimitriou | value = scanner.scanUntil(tagRes[0]);
|
394 | 4e03ba30 | Kostas Papadimitriou | if (value) {
|
395 | 4e03ba30 | Kostas Papadimitriou | for (var i = 0, len = value.length; i < len; ++i) { |
396 | 4e03ba30 | Kostas Papadimitriou | chr = value.charAt(i); |
397 | 4e03ba30 | Kostas Papadimitriou | |
398 | 4e03ba30 | Kostas Papadimitriou | if (isWhitespace(chr)) {
|
399 | 4e03ba30 | Kostas Papadimitriou | spaces.push(tokens.length); |
400 | 4e03ba30 | Kostas Papadimitriou | } else {
|
401 | 4e03ba30 | Kostas Papadimitriou | nonSpace = true;
|
402 | 4e03ba30 | Kostas Papadimitriou | } |
403 | 4e03ba30 | Kostas Papadimitriou | |
404 | 4e03ba30 | Kostas Papadimitriou | tokens.push(['text', chr, start, start + 1]); |
405 | 4e03ba30 | Kostas Papadimitriou | start += 1;
|
406 | 4e03ba30 | Kostas Papadimitriou | |
407 | 4e03ba30 | Kostas Papadimitriou | // Check for whitespace on the current line.
|
408 | 4e03ba30 | Kostas Papadimitriou | if (chr == '\n') stripSpace(); |
409 | 4e03ba30 | Kostas Papadimitriou | } |
410 | 4e03ba30 | Kostas Papadimitriou | } |
411 | 4e03ba30 | Kostas Papadimitriou | |
412 | 4e03ba30 | Kostas Papadimitriou | // Match the opening tag.
|
413 | 4e03ba30 | Kostas Papadimitriou | if (!scanner.scan(tagRes[0])) break; |
414 | 4e03ba30 | Kostas Papadimitriou | hasTag = true;
|
415 | 4e03ba30 | Kostas Papadimitriou | |
416 | 4e03ba30 | Kostas Papadimitriou | // Get the tag type.
|
417 | 4e03ba30 | Kostas Papadimitriou | type = scanner.scan(tagRe) || 'name';
|
418 | 4e03ba30 | Kostas Papadimitriou | scanner.scan(whiteRe); |
419 | 4e03ba30 | Kostas Papadimitriou | |
420 | 4e03ba30 | Kostas Papadimitriou | // Get the tag value.
|
421 | 4e03ba30 | Kostas Papadimitriou | if (type === '=') { |
422 | 4e03ba30 | Kostas Papadimitriou | value = scanner.scanUntil(eqRe); |
423 | 4e03ba30 | Kostas Papadimitriou | scanner.scan(eqRe); |
424 | 4e03ba30 | Kostas Papadimitriou | scanner.scanUntil(tagRes[1]);
|
425 | 4e03ba30 | Kostas Papadimitriou | } else if (type === '{') { |
426 | 4e03ba30 | Kostas Papadimitriou | value = scanner.scanUntil(new RegExp('\\s*' + escapeRe('}' + tags[1]))); |
427 | 4e03ba30 | Kostas Papadimitriou | scanner.scan(curlyRe); |
428 | 4e03ba30 | Kostas Papadimitriou | scanner.scanUntil(tagRes[1]);
|
429 | 4e03ba30 | Kostas Papadimitriou | type = '&';
|
430 | 4e03ba30 | Kostas Papadimitriou | } else {
|
431 | 4e03ba30 | Kostas Papadimitriou | value = scanner.scanUntil(tagRes[1]);
|
432 | 4e03ba30 | Kostas Papadimitriou | } |
433 | 4e03ba30 | Kostas Papadimitriou | |
434 | 4e03ba30 | Kostas Papadimitriou | // Match the closing tag.
|
435 | 4e03ba30 | Kostas Papadimitriou | if (!scanner.scan(tagRes[1])) throw new Error('Unclosed tag at ' + scanner.pos); |
436 | 4e03ba30 | Kostas Papadimitriou | |
437 | 4e03ba30 | Kostas Papadimitriou | token = [type, value, start, scanner.pos]; |
438 | 4e03ba30 | Kostas Papadimitriou | tokens.push(token); |
439 | 4e03ba30 | Kostas Papadimitriou | |
440 | 4e03ba30 | Kostas Papadimitriou | if (type === '#' || type === '^') { |
441 | 4e03ba30 | Kostas Papadimitriou | sections.push(token); |
442 | 4e03ba30 | Kostas Papadimitriou | } else if (type === '/') { |
443 | 4e03ba30 | Kostas Papadimitriou | // Check section nesting.
|
444 | 4e03ba30 | Kostas Papadimitriou | if (sections.length === 0) throw new Error('Unopened section "' + value + '" at ' + start); |
445 | 4e03ba30 | Kostas Papadimitriou | var openSection = sections.pop();
|
446 | 4e03ba30 | Kostas Papadimitriou | if (openSection[1] !== value) throw new Error('Unclosed section "' + openSection[1] + '" at ' + start); |
447 | 4e03ba30 | Kostas Papadimitriou | } else if (type === 'name' || type === '{' || type === '&') { |
448 | 4e03ba30 | Kostas Papadimitriou | nonSpace = true;
|
449 | 4e03ba30 | Kostas Papadimitriou | } else if (type === '=') { |
450 | 4e03ba30 | Kostas Papadimitriou | // Set the tags for the next time around.
|
451 | 4e03ba30 | Kostas Papadimitriou | tags = value.split(spaceRe); |
452 | 4e03ba30 | Kostas Papadimitriou | if (tags.length !== 2) throw new Error('Invalid tags at ' + start + ': ' + tags.join(', ')); |
453 | 4e03ba30 | Kostas Papadimitriou | tagRes = escapeTags(tags); |
454 | 4e03ba30 | Kostas Papadimitriou | } |
455 | 4e03ba30 | Kostas Papadimitriou | } |
456 | 4e03ba30 | Kostas Papadimitriou | |
457 | 4e03ba30 | Kostas Papadimitriou | // Make sure there are no open sections when we're done.
|
458 | 4e03ba30 | Kostas Papadimitriou | var openSection = sections.pop();
|
459 | 4e03ba30 | Kostas Papadimitriou | if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos); |
460 | 4e03ba30 | Kostas Papadimitriou | |
461 | 4e03ba30 | Kostas Papadimitriou | tokens = squashTokens(tokens); |
462 | 4e03ba30 | Kostas Papadimitriou | |
463 | 4e03ba30 | Kostas Papadimitriou | return nestTokens(tokens);
|
464 | 4e03ba30 | Kostas Papadimitriou | } |
465 | 4e03ba30 | Kostas Papadimitriou | |
466 | 4e03ba30 | Kostas Papadimitriou | var mustache = {};
|
467 | 4e03ba30 | Kostas Papadimitriou | |
468 | 4e03ba30 | Kostas Papadimitriou | mustache.name = "mustache.js";
|
469 | 4e03ba30 | Kostas Papadimitriou | mustache.version = "0.7.2";
|
470 | 4e03ba30 | Kostas Papadimitriou | mustache.tags = ["{{", "}}"]; |
471 | 4e03ba30 | Kostas Papadimitriou | |
472 | 4e03ba30 | Kostas Papadimitriou | mustache.Scanner = Scanner; |
473 | 4e03ba30 | Kostas Papadimitriou | mustache.Context = Context; |
474 | 4e03ba30 | Kostas Papadimitriou | mustache.Writer = Writer; |
475 | 4e03ba30 | Kostas Papadimitriou | |
476 | 4e03ba30 | Kostas Papadimitriou | mustache.parse = parseTemplate; |
477 | 4e03ba30 | Kostas Papadimitriou | |
478 | 4e03ba30 | Kostas Papadimitriou | // Export the escaping function so that the user may override it.
|
479 | 4e03ba30 | Kostas Papadimitriou | // See https://github.com/janl/mustache.js/issues/244
|
480 | 4e03ba30 | Kostas Papadimitriou | mustache.escape = escapeHtml; |
481 | 4e03ba30 | Kostas Papadimitriou | |
482 | 4e03ba30 | Kostas Papadimitriou | // All Mustache.* functions use this writer.
|
483 | 4e03ba30 | Kostas Papadimitriou | var _writer = new Writer(); |
484 | 4e03ba30 | Kostas Papadimitriou | |
485 | 4e03ba30 | Kostas Papadimitriou | /**
|
486 | 4e03ba30 | Kostas Papadimitriou | * Clears all cached templates and partials in the default writer.
|
487 | 4e03ba30 | Kostas Papadimitriou | */
|
488 | 4e03ba30 | Kostas Papadimitriou | mustache.clearCache = function () { |
489 | 4e03ba30 | Kostas Papadimitriou | return _writer.clearCache();
|
490 | 4e03ba30 | Kostas Papadimitriou | }; |
491 | 4e03ba30 | Kostas Papadimitriou | |
492 | 4e03ba30 | Kostas Papadimitriou | /**
|
493 | 4e03ba30 | Kostas Papadimitriou | * Compiles the given `template` to a reusable function using the default
|
494 | 4e03ba30 | Kostas Papadimitriou | * writer.
|
495 | 4e03ba30 | Kostas Papadimitriou | */
|
496 | 4e03ba30 | Kostas Papadimitriou | mustache.compile = function (template, tags) { |
497 | 4e03ba30 | Kostas Papadimitriou | return _writer.compile(template, tags);
|
498 | 4e03ba30 | Kostas Papadimitriou | }; |
499 | 4e03ba30 | Kostas Papadimitriou | |
500 | 4e03ba30 | Kostas Papadimitriou | /**
|
501 | 4e03ba30 | Kostas Papadimitriou | * Compiles the partial with the given `name` and `template` to a reusable
|
502 | 4e03ba30 | Kostas Papadimitriou | * function using the default writer.
|
503 | 4e03ba30 | Kostas Papadimitriou | */
|
504 | 4e03ba30 | Kostas Papadimitriou | mustache.compilePartial = function (name, template, tags) { |
505 | 4e03ba30 | Kostas Papadimitriou | return _writer.compilePartial(name, template, tags);
|
506 | 4e03ba30 | Kostas Papadimitriou | }; |
507 | 4e03ba30 | Kostas Papadimitriou | |
508 | 4e03ba30 | Kostas Papadimitriou | /**
|
509 | 4e03ba30 | Kostas Papadimitriou | * Compiles the given array of tokens (the output of a parse) to a reusable
|
510 | 4e03ba30 | Kostas Papadimitriou | * function using the default writer.
|
511 | 4e03ba30 | Kostas Papadimitriou | */
|
512 | 4e03ba30 | Kostas Papadimitriou | mustache.compileTokens = function (tokens, template) { |
513 | 4e03ba30 | Kostas Papadimitriou | return _writer.compileTokens(tokens, template);
|
514 | 4e03ba30 | Kostas Papadimitriou | }; |
515 | 4e03ba30 | Kostas Papadimitriou | |
516 | 4e03ba30 | Kostas Papadimitriou | /**
|
517 | 4e03ba30 | Kostas Papadimitriou | * Renders the `template` with the given `view` and `partials` using the
|
518 | 4e03ba30 | Kostas Papadimitriou | * default writer.
|
519 | 4e03ba30 | Kostas Papadimitriou | */
|
520 | 4e03ba30 | Kostas Papadimitriou | mustache.render = function (template, view, partials) { |
521 | 4e03ba30 | Kostas Papadimitriou | return _writer.render(template, view, partials);
|
522 | 4e03ba30 | Kostas Papadimitriou | }; |
523 | 4e03ba30 | Kostas Papadimitriou | |
524 | 4e03ba30 | Kostas Papadimitriou | // This is here for backwards compatibility with 0.4.x.
|
525 | 4e03ba30 | Kostas Papadimitriou | mustache.to_html = function (template, view, partials, send) { |
526 | 4e03ba30 | Kostas Papadimitriou | var result = mustache.render(template, view, partials);
|
527 | 4e03ba30 | Kostas Papadimitriou | |
528 | 4e03ba30 | Kostas Papadimitriou | if (typeof send === "function") { |
529 | 4e03ba30 | Kostas Papadimitriou | send(result); |
530 | 4e03ba30 | Kostas Papadimitriou | } else {
|
531 | 4e03ba30 | Kostas Papadimitriou | return result;
|
532 | 4e03ba30 | Kostas Papadimitriou | } |
533 | 4e03ba30 | Kostas Papadimitriou | }; |
534 | 4e03ba30 | Kostas Papadimitriou | |
535 | 4e03ba30 | Kostas Papadimitriou | return mustache;
|
536 | 4e03ba30 | Kostas Papadimitriou | |
537 | 4e03ba30 | Kostas Papadimitriou | }()))); |