| author | ymh <ymh.work@gmail.com> |
| Sat, 19 Oct 2024 01:34:55 +0200 | |
| changeset 1075 | 92cb33eb7a75 |
| parent 1057 | 3f20f286d43e |
| permissions | -rw-r--r-- |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
1 |
/*! |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
2 |
* mustache.js - Logic-less {{mustache}} templates with JavaScript |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
3 |
* http://github.com/janl/mustache.js |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
4 |
*/ |
| 110 | 5 |
|
| 1057 | 6 |
/*global define: false Mustache: true*/ |
| 110 | 7 |
|
| 1057 | 8 |
(function defineMustache (global, factory) { |
9 |
if (typeof exports === 'object' && exports && typeof exports.nodeName !== 'string') { |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
10 |
factory(exports); // CommonJS |
| 1057 | 11 |
} else if (typeof define === 'function' && define.amd) { |
12 |
define(['exports'], factory); // AMD |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
13 |
} else { |
| 1057 | 14 |
global.Mustache = {}; |
15 |
factory(Mustache); // script, wsh, asp |
|
16 |
} |
|
17 |
}(this, function mustacheFactory (mustache) { |
|
18 |
||
19 |
var objectToString = Object.prototype.toString; |
|
20 |
var isArray = Array.isArray || function isArrayPolyfill (object) { |
|
21 |
return objectToString.call(object) === '[object Array]'; |
|
22 |
}; |
|
23 |
||
24 |
function isFunction (object) { |
|
25 |
return typeof object === 'function'; |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
26 |
} |
| 1057 | 27 |
|
28 |
/** |
|
29 |
* More correct typeof string handling array |
|
30 |
* which normally returns typeof 'object' |
|
31 |
*/ |
|
32 |
function typeStr (obj) { |
|
33 |
return isArray(obj) ? 'array' : typeof obj; |
|
34 |
} |
|
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
35 |
|
| 1057 | 36 |
function escapeRegExp (string) { |
37 |
return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); |
|
38 |
} |
|
39 |
||
40 |
/** |
|
41 |
* Null safe way of checking whether or not an object, |
|
42 |
* including its prototype, has a given property |
|
43 |
*/ |
|
44 |
function hasProperty (obj, propName) { |
|
45 |
return obj != null && typeof obj === 'object' && (propName in obj); |
|
46 |
} |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
47 |
|
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
48 |
// Workaround for https://issues.apache.org/jira/browse/COUCHDB-577 |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
49 |
// See https://github.com/janl/mustache.js/issues/189 |
| 1057 | 50 |
var regExpTest = RegExp.prototype.test; |
51 |
function testRegExp (re, string) { |
|
52 |
return regExpTest.call(re, string); |
|
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
53 |
} |
|
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
54 |
|
| 1057 | 55 |
var nonSpaceRe = /\S/; |
56 |
function isWhitespace (string) { |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
57 |
return !testRegExp(nonSpaceRe, string); |
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
58 |
} |
|
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
59 |
|
| 1057 | 60 |
var entityMap = { |
61 |
'&': '&', |
|
62 |
'<': '<', |
|
63 |
'>': '>', |
|
64 |
'"': '"', |
|
65 |
"'": ''', |
|
66 |
'/': '/' |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
67 |
}; |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
68 |
|
| 1057 | 69 |
function escapeHtml (string) { |
70 |
return String(string).replace(/[&<>"'\/]/g, function fromEntityMap (s) { |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
71 |
return entityMap[s]; |
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
72 |
}); |
|
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
73 |
} |
|
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
74 |
|
| 1057 | 75 |
var whiteRe = /\s*/; |
76 |
var spaceRe = /\s+/; |
|
77 |
var equalsRe = /\s*=/; |
|
78 |
var curlyRe = /\s*\}/; |
|
79 |
var tagRe = /#|\^|\/|>|\{|&|=|!/; |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
80 |
|
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
81 |
/** |
| 1057 | 82 |
* Breaks up the given `template` string into a tree of tokens. If the `tags` |
83 |
* argument is given here it must be an array with two string values: the |
|
84 |
* opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of |
|
85 |
* course, the default is to use mustaches (i.e. mustache.tags). |
|
86 |
* |
|
87 |
* A token is an array with at least 4 elements. The first element is the |
|
88 |
* mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag |
|
89 |
* did not contain a symbol (i.e. {{myValue}}) this element is "name". For |
|
90 |
* all text that appears outside a symbol this element is "text". |
|
91 |
* |
|
92 |
* The second element of a token is its "value". For mustache tags this is |
|
93 |
* whatever else was inside the tag besides the opening symbol. For text tokens |
|
94 |
* this is the text itself. |
|
95 |
* |
|
96 |
* The third and fourth elements of the token are the start and end indices, |
|
97 |
* respectively, of the token in the original template. |
|
98 |
* |
|
99 |
* Tokens that are the root node of a subtree contain two more elements: 1) an |
|
100 |
* array of tokens in the subtree and 2) the index in the original template at |
|
101 |
* which the closing tag for that section begins. |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
102 |
*/ |
| 1057 | 103 |
function parseTemplate (template, tags) { |
104 |
if (!template) |
|
105 |
return []; |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
106 |
|
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
107 |
var sections = []; // Stack to hold section tokens |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
108 |
var tokens = []; // Buffer to hold the tokens |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
109 |
var spaces = []; // Indices of whitespace tokens on the current line |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
110 |
var hasTag = false; // Is there a {{tag}} on the current line? |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
111 |
var nonSpace = false; // Is there a non-space char on the current line? |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
112 |
|
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
113 |
// Strips all whitespace tokens array for the current line |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
114 |
// if there was a {{#tag}} on it and otherwise only space. |
| 1057 | 115 |
function stripSpace () { |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
116 |
if (hasTag && !nonSpace) { |
| 1057 | 117 |
while (spaces.length) |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
118 |
delete tokens[spaces.pop()]; |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
119 |
} else { |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
120 |
spaces = []; |
| 110 | 121 |
} |
122 |
||
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
123 |
hasTag = false; |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
124 |
nonSpace = false; |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
125 |
} |
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
126 |
|
| 1057 | 127 |
var openingTagRe, closingTagRe, closingCurlyRe; |
128 |
function compileTags (tagsToCompile) { |
|
129 |
if (typeof tagsToCompile === 'string') |
|
130 |
tagsToCompile = tagsToCompile.split(spaceRe, 2); |
|
131 |
||
132 |
if (!isArray(tagsToCompile) || tagsToCompile.length !== 2) |
|
133 |
throw new Error('Invalid tags: ' + tagsToCompile); |
|
134 |
||
135 |
openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*'); |
|
136 |
closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1])); |
|
137 |
closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1])); |
|
138 |
} |
|
139 |
||
140 |
compileTags(tags || mustache.tags); |
|
141 |
||
142 |
var scanner = new Scanner(template); |
|
143 |
||
144 |
var start, type, value, chr, token, openSection; |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
145 |
while (!scanner.eos()) { |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
146 |
start = scanner.pos; |
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
147 |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
148 |
// Match any text between tags. |
| 1057 | 149 |
value = scanner.scanUntil(openingTagRe); |
150 |
||
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
151 |
if (value) { |
| 1057 | 152 |
for (var i = 0, valueLength = value.length; i < valueLength; ++i) { |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
153 |
chr = value.charAt(i); |
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
154 |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
155 |
if (isWhitespace(chr)) { |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
156 |
spaces.push(tokens.length); |
| 110 | 157 |
} else { |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
158 |
nonSpace = true; |
| 110 | 159 |
} |
160 |
||
| 1057 | 161 |
tokens.push([ 'text', chr, start, start + 1 ]); |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
162 |
start += 1; |
| 110 | 163 |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
164 |
// Check for whitespace on the current line. |
| 1057 | 165 |
if (chr === '\n') |
166 |
stripSpace(); |
|
| 110 | 167 |
} |
168 |
} |
|
169 |
||
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
170 |
// Match the opening tag. |
| 1057 | 171 |
if (!scanner.scan(openingTagRe)) |
172 |
break; |
|
173 |
||
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
174 |
hasTag = true; |
| 110 | 175 |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
176 |
// Get the tag type. |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
177 |
type = scanner.scan(tagRe) || 'name'; |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
178 |
scanner.scan(whiteRe); |
| 110 | 179 |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
180 |
// Get the tag value. |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
181 |
if (type === '=') { |
| 1057 | 182 |
value = scanner.scanUntil(equalsRe); |
183 |
scanner.scan(equalsRe); |
|
184 |
scanner.scanUntil(closingTagRe); |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
185 |
} else if (type === '{') { |
| 1057 | 186 |
value = scanner.scanUntil(closingCurlyRe); |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
187 |
scanner.scan(curlyRe); |
| 1057 | 188 |
scanner.scanUntil(closingTagRe); |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
189 |
type = '&'; |
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
190 |
} else { |
| 1057 | 191 |
value = scanner.scanUntil(closingTagRe); |
| 110 | 192 |
} |
193 |
||
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
194 |
// Match the closing tag. |
| 1057 | 195 |
if (!scanner.scan(closingTagRe)) |
196 |
throw new Error('Unclosed tag at ' + scanner.pos); |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
197 |
|
| 1057 | 198 |
token = [ type, value, start, scanner.pos ]; |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
199 |
tokens.push(token); |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
200 |
|
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
201 |
if (type === '#' || type === '^') { |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
202 |
sections.push(token); |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
203 |
} else if (type === '/') { |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
204 |
// Check section nesting. |
| 1057 | 205 |
openSection = sections.pop(); |
206 |
||
207 |
if (!openSection) |
|
208 |
throw new Error('Unopened section "' + value + '" at ' + start); |
|
209 |
||
210 |
if (openSection[1] !== value) |
|
211 |
throw new Error('Unclosed section "' + openSection[1] + '" at ' + start); |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
212 |
} else if (type === 'name' || type === '{' || type === '&') { |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
213 |
nonSpace = true; |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
214 |
} else if (type === '=') { |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
215 |
// Set the tags for the next time around. |
| 1057 | 216 |
compileTags(value); |
| 110 | 217 |
} |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
218 |
} |
| 110 | 219 |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
220 |
// Make sure there are no open sections when we're done. |
| 1057 | 221 |
openSection = sections.pop(); |
222 |
||
223 |
if (openSection) |
|
224 |
throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos); |
|
225 |
||
226 |
return nestTokens(squashTokens(tokens)); |
|
227 |
} |
|
228 |
||
229 |
/** |
|
230 |
* Combines the values of consecutive text tokens in the given `tokens` array |
|
231 |
* to a single token. |
|
232 |
*/ |
|
233 |
function squashTokens (tokens) { |
|
234 |
var squashedTokens = []; |
|
235 |
||
236 |
var token, lastToken; |
|
237 |
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { |
|
238 |
token = tokens[i]; |
|
239 |
||
240 |
if (token) { |
|
241 |
if (token[0] === 'text' && lastToken && lastToken[0] === 'text') { |
|
242 |
lastToken[1] += token[1]; |
|
243 |
lastToken[3] = token[3]; |
|
244 |
} else { |
|
245 |
squashedTokens.push(token); |
|
246 |
lastToken = token; |
|
247 |
} |
|
248 |
} |
|
249 |
} |
|
250 |
||
251 |
return squashedTokens; |
|
252 |
} |
|
253 |
||
254 |
/** |
|
255 |
* Forms the given array of `tokens` into a nested tree structure where |
|
256 |
* tokens that represent a section have two additional items: 1) an array of |
|
257 |
* all tokens that appear in that section and 2) the index in the original |
|
258 |
* template that represents the end of that section. |
|
259 |
*/ |
|
260 |
function nestTokens (tokens) { |
|
261 |
var nestedTokens = []; |
|
262 |
var collector = nestedTokens; |
|
263 |
var sections = []; |
|
264 |
||
265 |
var token, section; |
|
266 |
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { |
|
267 |
token = tokens[i]; |
|
268 |
||
269 |
switch (token[0]) { |
|
270 |
case '#': |
|
271 |
case '^': |
|
272 |
collector.push(token); |
|
273 |
sections.push(token); |
|
274 |
collector = token[4] = []; |
|
275 |
break; |
|
276 |
case '/': |
|
277 |
section = sections.pop(); |
|
278 |
section[5] = token[2]; |
|
279 |
collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens; |
|
280 |
break; |
|
281 |
default: |
|
282 |
collector.push(token); |
|
283 |
} |
|
284 |
} |
|
285 |
||
286 |
return nestedTokens; |
|
287 |
} |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
288 |
|
| 1057 | 289 |
/** |
290 |
* A simple string scanner that is used by the template parser to find |
|
291 |
* tokens in template strings. |
|
292 |
*/ |
|
293 |
function Scanner (string) { |
|
294 |
this.string = string; |
|
295 |
this.tail = string; |
|
296 |
this.pos = 0; |
|
297 |
} |
|
298 |
||
299 |
/** |
|
300 |
* Returns `true` if the tail is empty (end of string). |
|
301 |
*/ |
|
302 |
Scanner.prototype.eos = function eos () { |
|
303 |
return this.tail === ''; |
|
304 |
}; |
|
305 |
||
306 |
/** |
|
307 |
* Tries to match the given regular expression at the current position. |
|
308 |
* Returns the matched text if it can match, the empty string otherwise. |
|
309 |
*/ |
|
310 |
Scanner.prototype.scan = function scan (re) { |
|
311 |
var match = this.tail.match(re); |
|
312 |
||
313 |
if (!match || match.index !== 0) |
|
314 |
return ''; |
|
315 |
||
316 |
var string = match[0]; |
|
317 |
||
318 |
this.tail = this.tail.substring(string.length); |
|
319 |
this.pos += string.length; |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
320 |
|
| 1057 | 321 |
return string; |
322 |
}; |
|
323 |
||
324 |
/** |
|
325 |
* Skips all text until the given regular expression can be matched. Returns |
|
326 |
* the skipped string, which is the entire tail if no match can be made. |
|
327 |
*/ |
|
328 |
Scanner.prototype.scanUntil = function scanUntil (re) { |
|
329 |
var index = this.tail.search(re), match; |
|
330 |
||
331 |
switch (index) { |
|
332 |
case -1: |
|
333 |
match = this.tail; |
|
334 |
this.tail = ''; |
|
335 |
break; |
|
336 |
case 0: |
|
337 |
match = ''; |
|
338 |
break; |
|
339 |
default: |
|
340 |
match = this.tail.substring(0, index); |
|
341 |
this.tail = this.tail.substring(index); |
|
342 |
} |
|
343 |
||
344 |
this.pos += match.length; |
|
345 |
||
346 |
return match; |
|
347 |
}; |
|
348 |
||
349 |
/** |
|
350 |
* Represents a rendering context by wrapping a view object and |
|
351 |
* maintaining a reference to the parent context. |
|
352 |
*/ |
|
353 |
function Context (view, parentContext) { |
|
354 |
this.view = view; |
|
355 |
this.cache = { '.': this.view }; |
|
356 |
this.parent = parentContext; |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
357 |
} |
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
358 |
|
| 1057 | 359 |
/** |
360 |
* Creates a new context using the given view with this context |
|
361 |
* as the parent. |
|
362 |
*/ |
|
363 |
Context.prototype.push = function push (view) { |
|
364 |
return new Context(view, this); |
|
365 |
}; |
|
366 |
||
367 |
/** |
|
368 |
* Returns the value of the given name in this context, traversing |
|
369 |
* up the context hierarchy if the value is absent in this context's view. |
|
370 |
*/ |
|
371 |
Context.prototype.lookup = function lookup (name) { |
|
372 |
var cache = this.cache; |
|
373 |
||
374 |
var value; |
|
375 |
if (cache.hasOwnProperty(name)) { |
|
376 |
value = cache[name]; |
|
377 |
} else { |
|
378 |
var context = this, names, index, lookupHit = false; |
|
379 |
||
380 |
while (context) { |
|
381 |
if (name.indexOf('.') > 0) { |
|
382 |
value = context.view; |
|
383 |
names = name.split('.'); |
|
384 |
index = 0; |
|
385 |
||
386 |
/** |
|
387 |
* Using the dot notion path in `name`, we descend through the |
|
388 |
* nested objects. |
|
389 |
* |
|
390 |
* To be certain that the lookup has been successful, we have to |
|
391 |
* check if the last object in the path actually has the property |
|
392 |
* we are looking for. We store the result in `lookupHit`. |
|
393 |
* |
|
394 |
* This is specially necessary for when the value has been set to |
|
395 |
* `undefined` and we want to avoid looking up parent contexts. |
|
396 |
**/ |
|
397 |
while (value != null && index < names.length) { |
|
398 |
if (index === names.length - 1) |
|
399 |
lookupHit = hasProperty(value, names[index]); |
|
| 110 | 400 |
|
| 1057 | 401 |
value = value[names[index++]]; |
402 |
} |
|
403 |
} else { |
|
404 |
value = context.view[name]; |
|
405 |
lookupHit = hasProperty(context.view, name); |
|
406 |
} |
|
407 |
||
408 |
if (lookupHit) |
|
409 |
break; |
|
410 |
||
411 |
context = context.parent; |
|
412 |
} |
|
413 |
||
414 |
cache[name] = value; |
|
415 |
} |
|
416 |
||
417 |
if (isFunction(value)) |
|
418 |
value = value.call(this.view); |
|
419 |
||
420 |
return value; |
|
421 |
}; |
|
422 |
||
423 |
/** |
|
424 |
* A Writer knows how to take a stream of tokens and render them to a |
|
425 |
* string, given a context. It also maintains a cache of templates to |
|
426 |
* avoid the need to parse the same template twice. |
|
427 |
*/ |
|
428 |
function Writer () { |
|
429 |
this.cache = {}; |
|
430 |
} |
|
431 |
||
432 |
/** |
|
433 |
* Clears all cached templates in this writer. |
|
434 |
*/ |
|
435 |
Writer.prototype.clearCache = function clearCache () { |
|
436 |
this.cache = {}; |
|
437 |
}; |
|
438 |
||
439 |
/** |
|
440 |
* Parses and caches the given `template` and returns the array of tokens |
|
441 |
* that is generated from the parse. |
|
442 |
*/ |
|
443 |
Writer.prototype.parse = function parse (template, tags) { |
|
444 |
var cache = this.cache; |
|
445 |
var tokens = cache[template]; |
|
446 |
||
447 |
if (tokens == null) |
|
448 |
tokens = cache[template] = parseTemplate(template, tags); |
|
449 |
||
450 |
return tokens; |
|
451 |
}; |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
452 |
|
| 1057 | 453 |
/** |
454 |
* High-level method that is used to render the given `template` with |
|
455 |
* the given `view`. |
|
456 |
* |
|
457 |
* The optional `partials` argument may be an object that contains the |
|
458 |
* names and templates of partials that are used in the template. It may |
|
459 |
* also be a function that is used to load partial templates on the fly |
|
460 |
* that takes a single argument: the name of the partial. |
|
461 |
*/ |
|
462 |
Writer.prototype.render = function render (template, view, partials) { |
|
463 |
var tokens = this.parse(template); |
|
464 |
var context = (view instanceof Context) ? view : new Context(view); |
|
465 |
return this.renderTokens(tokens, context, partials, template); |
|
466 |
}; |
|
467 |
||
468 |
/** |
|
469 |
* Low-level method that renders the given array of `tokens` using |
|
470 |
* the given `context` and `partials`. |
|
471 |
* |
|
472 |
* Note: The `originalTemplate` is only ever used to extract the portion |
|
473 |
* of the original template that was contained in a higher-order section. |
|
474 |
* If the template doesn't use higher-order sections, this argument may |
|
475 |
* be omitted. |
|
476 |
*/ |
|
477 |
Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate) { |
|
478 |
var buffer = ''; |
|
479 |
||
480 |
var token, symbol, value; |
|
481 |
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { |
|
482 |
value = undefined; |
|
483 |
token = tokens[i]; |
|
484 |
symbol = token[0]; |
|
485 |
||
486 |
if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate); |
|
487 |
else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate); |
|
488 |
else if (symbol === '>') value = this.renderPartial(token, context, partials, originalTemplate); |
|
489 |
else if (symbol === '&') value = this.unescapedValue(token, context); |
|
490 |
else if (symbol === 'name') value = this.escapedValue(token, context); |
|
491 |
else if (symbol === 'text') value = this.rawValue(token); |
|
492 |
||
493 |
if (value !== undefined) |
|
494 |
buffer += value; |
|
495 |
} |
|
496 |
||
497 |
return buffer; |
|
498 |
}; |
|
499 |
||
500 |
Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) { |
|
501 |
var self = this; |
|
502 |
var buffer = ''; |
|
503 |
var value = context.lookup(token[1]); |
|
504 |
||
505 |
// This function is used to render an arbitrary template |
|
506 |
// in the current context by higher-order sections. |
|
507 |
function subRender (template) { |
|
508 |
return self.render(template, context, partials); |
|
509 |
} |
|
510 |
||
511 |
if (!value) return; |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
512 |
|
| 1057 | 513 |
if (isArray(value)) { |
514 |
for (var j = 0, valueLength = value.length; j < valueLength; ++j) { |
|
515 |
buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate); |
|
516 |
} |
|
517 |
} else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') { |
|
518 |
buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate); |
|
519 |
} else if (isFunction(value)) { |
|
520 |
if (typeof originalTemplate !== 'string') |
|
521 |
throw new Error('Cannot use higher-order sections without the original template'); |
|
522 |
||
523 |
// Extract the portion of the original template that the section contains. |
|
524 |
value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender); |
|
525 |
||
526 |
if (value != null) |
|
527 |
buffer += value; |
|
528 |
} else { |
|
529 |
buffer += this.renderTokens(token[4], context, partials, originalTemplate); |
|
530 |
} |
|
531 |
return buffer; |
|
532 |
}; |
|
533 |
||
534 |
Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) { |
|
535 |
var value = context.lookup(token[1]); |
|
536 |
||
537 |
// Use JavaScript's definition of falsy. Include empty arrays. |
|
538 |
// See https://github.com/janl/mustache.js/issues/186 |
|
539 |
if (!value || (isArray(value) && value.length === 0)) |
|
540 |
return this.renderTokens(token[4], context, partials, originalTemplate); |
|
541 |
}; |
|
| 110 | 542 |
|
| 1057 | 543 |
Writer.prototype.renderPartial = function renderPartial (token, context, partials) { |
544 |
if (!partials) return; |
|
545 |
||
546 |
var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; |
|
547 |
if (value != null) |
|
548 |
return this.renderTokens(this.parse(value), context, partials, value); |
|
549 |
}; |
|
550 |
||
551 |
Writer.prototype.unescapedValue = function unescapedValue (token, context) { |
|
552 |
var value = context.lookup(token[1]); |
|
553 |
if (value != null) |
|
554 |
return value; |
|
555 |
}; |
|
556 |
||
557 |
Writer.prototype.escapedValue = function escapedValue (token, context) { |
|
558 |
var value = context.lookup(token[1]); |
|
559 |
if (value != null) |
|
560 |
return mustache.escape(value); |
|
561 |
}; |
|
562 |
||
563 |
Writer.prototype.rawValue = function rawValue (token) { |
|
564 |
return token[1]; |
|
565 |
}; |
|
566 |
||
567 |
mustache.name = 'mustache.js'; |
|
568 |
mustache.version = '2.1.3'; |
|
569 |
mustache.tags = [ '{{', '}}' ]; |
|
570 |
||
571 |
// All high-level mustache.* functions use this writer. |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
572 |
var defaultWriter = new Writer(); |
| 110 | 573 |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
574 |
/** |
| 1057 | 575 |
* Clears all cached templates in the default writer. |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
576 |
*/ |
| 1057 | 577 |
mustache.clearCache = function clearCache () { |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
578 |
return defaultWriter.clearCache(); |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
579 |
}; |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
580 |
|
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
581 |
/** |
| 1057 | 582 |
* Parses and caches the given template in the default writer and returns the |
583 |
* array of tokens it contains. Doing this ahead of time avoids the need to |
|
584 |
* parse templates on the fly as they are rendered. |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
585 |
*/ |
| 1057 | 586 |
mustache.parse = function parse (template, tags) { |
587 |
return defaultWriter.parse(template, tags); |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
588 |
}; |
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
589 |
|
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
590 |
/** |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
591 |
* Renders the `template` with the given `view` and `partials` using the |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
592 |
* default writer. |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
593 |
*/ |
| 1057 | 594 |
mustache.render = function render (template, view, partials) { |
595 |
if (typeof template !== 'string') { |
|
596 |
throw new TypeError('Invalid template! Template should be a "string" ' + |
|
597 |
'but "' + typeStr(template) + '" was given as the first ' + |
|
598 |
'argument for mustache#render(template, view, partials)'); |
|
599 |
} |
|
600 |
||
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
601 |
return defaultWriter.render(template, view, partials); |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
602 |
}; |
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
603 |
|
| 1057 | 604 |
// This is here for backwards compatibility with 0.4.x., |
605 |
/*eslint-disable */ // eslint wants camel cased function name |
|
606 |
mustache.to_html = function to_html (template, view, partials, send) { |
|
607 |
/*eslint-enable*/ |
|
608 |
||
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
609 |
var result = mustache.render(template, view, partials); |
|
583
310f5517a2ea
bumped mustache.js version to get access to the new features.
hamidouk
parents:
110
diff
changeset
|
610 |
|
| 1057 | 611 |
if (isFunction(send)) { |
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
612 |
send(result); |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
613 |
} else { |
|
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
614 |
return result; |
| 110 | 615 |
} |
616 |
}; |
|
617 |
||
| 1057 | 618 |
// Export the escaping function so that the user may override it. |
619 |
// See https://github.com/janl/mustache.js/issues/244 |
|
620 |
mustache.escape = escapeHtml; |
|
621 |
||
622 |
// Export these mainly for testing, but also for advanced usage. |
|
623 |
mustache.Scanner = Scanner; |
|
624 |
mustache.Context = Context; |
|
625 |
mustache.Writer = Writer; |
|
626 |
||
|
1001
3210bf928a11
Enabled loading widgets without the widgeting framework
veltr
parents:
583
diff
changeset
|
627 |
})); |