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