mustache-js-rails 3.1.0 → 4.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e571a562cb620ea6b136e61db57909e64d8d720934d8eb6aa4a2138955b74e3
4
- data.tar.gz: 70bcc946012dea4a264c343797f9ce6b7c30052ba7670ed3f0dd3903575a945f
3
+ metadata.gz: 608492ca76157606a99755d687f1f2eb8d5afa0ed7e4b64628509e3c50dd662f
4
+ data.tar.gz: 3b25eadfbab5a5d9729d470fb429a06b0423c94ed8bb7f7ba1a502fbd34a624d
5
5
  SHA512:
6
- metadata.gz: 0a4c07e01ba65011acfa8ff2cd366da459a97250212abe4a9f721b57f6ce0d8c5931944e541af7a285c9067076f0fc4e52093a4b286118ca28bfbb7fe0f93b13
7
- data.tar.gz: 21d41e2e0bfa9869f1b746a8a50f32a0e49f1cfed987787b2667385bc1e9a66c8eaf190f04c761e7efa9877c942174ae7514eda7d16f1035a4760115172012f6
6
+ metadata.gz: 8a640c2ea766c6bda447fb91cce601cd30f97deed450c2dced8be27ad4fbe9fc3b6dd307c4d62f76bc016c9662464ab1646f3d58c57b3e29d25244b19bd1dc7c
7
+ data.tar.gz: '0369c41d8fb4b92d64a33a1dc64d8dd23a77aad510d1893b5dd648c1baceb0d5a06f590b488210fe5f1529e879231ee385e62bb4f85d3421b6e14d723c6838ec'
data/README.md CHANGED
@@ -1,11 +1,15 @@
1
1
  # mustache-js-rails
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/mustache-js-rails.svg)][gem_version]
4
+
5
+ [gem_version]: https://rubygems.org/gems/mustache-js-rails
6
+
3
7
  mustache-js-rails integrates [mustache.js](https://github.com/janl/mustache.js)
4
8
  and [mustache jQuery integration](https://github.com/jonnyreeves/jquery-Mustache) with rails 3.1+ asset pipeline.
5
9
 
6
10
  Integrated versions are:
7
11
 
8
- * mustache.js - <b id="mustache-js-version">3.1.0</b>
12
+ * mustache.js - <b id="mustache-js-version">4.2.0</b>
9
13
  * jQuery mustache - <b id="jquery-mustache-js-version">0.2.8</b>
10
14
 
11
15
  ### Installation
@@ -1,3 +1,3 @@
1
1
  module MustacheJsRails
2
- VERSION = "3.1.0"
2
+ VERSION = "4.1.0.1"
3
3
  end
@@ -3,719 +3,762 @@
3
3
  * http://github.com/janl/mustache.js
4
4
  */
5
5
 
6
- /*global define: false Mustache: true*/
6
+ var objectToString = Object.prototype.toString;
7
+ var isArray = Array.isArray || function isArrayPolyfill (object) {
8
+ return objectToString.call(object) === '[object Array]';
9
+ };
10
+
11
+ function isFunction (object) {
12
+ return typeof object === 'function';
13
+ }
14
+
15
+ /**
16
+ * More correct typeof string handling array
17
+ * which normally returns typeof 'object'
18
+ */
19
+ function typeStr (obj) {
20
+ return isArray(obj) ? 'array' : typeof obj;
21
+ }
7
22
 
8
- (function defineMustache (global, factory) {
9
- if (typeof exports === 'object' && exports && typeof exports.nodeName !== 'string') {
10
- factory(exports); // CommonJS
11
- } else if (typeof define === 'function' && define.amd) {
12
- define(['exports'], factory); // AMD
13
- } else {
14
- global.Mustache = {};
15
- factory(global.Mustache); // script, wsh, asp
16
- }
17
- }(this, function mustacheFactory (mustache) {
23
+ function escapeRegExp (string) {
24
+ return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
25
+ }
18
26
 
19
- var objectToString = Object.prototype.toString;
20
- var isArray = Array.isArray || function isArrayPolyfill (object) {
21
- return objectToString.call(object) === '[object Array]';
22
- };
27
+ /**
28
+ * Null safe way of checking whether or not an object,
29
+ * including its prototype, has a given property
30
+ */
31
+ function hasProperty (obj, propName) {
32
+ return obj != null && typeof obj === 'object' && (propName in obj);
33
+ }
23
34
 
24
- function isFunction (object) {
25
- return typeof object === 'function';
26
- }
35
+ /**
36
+ * Safe way of detecting whether or not the given thing is a primitive and
37
+ * whether it has the given property
38
+ */
39
+ function primitiveHasOwnProperty (primitive, propName) {
40
+ return (
41
+ primitive != null
42
+ && typeof primitive !== 'object'
43
+ && primitive.hasOwnProperty
44
+ && primitive.hasOwnProperty(propName)
45
+ );
46
+ }
47
+
48
+ // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
49
+ // See https://github.com/janl/mustache.js/issues/189
50
+ var regExpTest = RegExp.prototype.test;
51
+ function testRegExp (re, string) {
52
+ return regExpTest.call(re, string);
53
+ }
54
+
55
+ var nonSpaceRe = /\S/;
56
+ function isWhitespace (string) {
57
+ return !testRegExp(nonSpaceRe, string);
58
+ }
59
+
60
+ var entityMap = {
61
+ '&': '&amp;',
62
+ '<': '&lt;',
63
+ '>': '&gt;',
64
+ '"': '&quot;',
65
+ "'": '&#39;',
66
+ '/': '&#x2F;',
67
+ '`': '&#x60;',
68
+ '=': '&#x3D;'
69
+ };
70
+
71
+ function escapeHtml (string) {
72
+ return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) {
73
+ return entityMap[s];
74
+ });
75
+ }
76
+
77
+ var whiteRe = /\s*/;
78
+ var spaceRe = /\s+/;
79
+ var equalsRe = /\s*=/;
80
+ var curlyRe = /\s*\}/;
81
+ var tagRe = /#|\^|\/|>|\{|&|=|!/;
82
+
83
+ /**
84
+ * Breaks up the given `template` string into a tree of tokens. If the `tags`
85
+ * argument is given here it must be an array with two string values: the
86
+ * opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of
87
+ * course, the default is to use mustaches (i.e. mustache.tags).
88
+ *
89
+ * A token is an array with at least 4 elements. The first element is the
90
+ * mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag
91
+ * did not contain a symbol (i.e. {{myValue}}) this element is "name". For
92
+ * all text that appears outside a symbol this element is "text".
93
+ *
94
+ * The second element of a token is its "value". For mustache tags this is
95
+ * whatever else was inside the tag besides the opening symbol. For text tokens
96
+ * this is the text itself.
97
+ *
98
+ * The third and fourth elements of the token are the start and end indices,
99
+ * respectively, of the token in the original template.
100
+ *
101
+ * Tokens that are the root node of a subtree contain two more elements: 1) an
102
+ * array of tokens in the subtree and 2) the index in the original template at
103
+ * which the closing tag for that section begins.
104
+ *
105
+ * Tokens for partials also contain two more elements: 1) a string value of
106
+ * indendation prior to that tag and 2) the index of that tag on that line -
107
+ * eg a value of 2 indicates the partial is the third tag on this line.
108
+ */
109
+ function parseTemplate (template, tags) {
110
+ if (!template)
111
+ return [];
112
+ var lineHasNonSpace = false;
113
+ var sections = []; // Stack to hold section tokens
114
+ var tokens = []; // Buffer to hold the tokens
115
+ var spaces = []; // Indices of whitespace tokens on the current line
116
+ var hasTag = false; // Is there a {{tag}} on the current line?
117
+ var nonSpace = false; // Is there a non-space char on the current line?
118
+ var indentation = ''; // Tracks indentation for tags that use it
119
+ var tagIndex = 0; // Stores a count of number of tags encountered on a line
120
+
121
+ // Strips all whitespace tokens array for the current line
122
+ // if there was a {{#tag}} on it and otherwise only space.
123
+ function stripSpace () {
124
+ if (hasTag && !nonSpace) {
125
+ while (spaces.length)
126
+ delete tokens[spaces.pop()];
127
+ } else {
128
+ spaces = [];
129
+ }
27
130
 
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;
131
+ hasTag = false;
132
+ nonSpace = false;
34
133
  }
35
134
 
36
- function escapeRegExp (string) {
37
- return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
38
- }
135
+ var openingTagRe, closingTagRe, closingCurlyRe;
136
+ function compileTags (tagsToCompile) {
137
+ if (typeof tagsToCompile === 'string')
138
+ tagsToCompile = tagsToCompile.split(spaceRe, 2);
39
139
 
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
- }
140
+ if (!isArray(tagsToCompile) || tagsToCompile.length !== 2)
141
+ throw new Error('Invalid tags: ' + tagsToCompile);
47
142
 
48
- /**
49
- * Safe way of detecting whether or not the given thing is a primitive and
50
- * whether it has the given property
51
- */
52
- function primitiveHasOwnProperty (primitive, propName) {
53
- return (
54
- primitive != null
55
- && typeof primitive !== 'object'
56
- && primitive.hasOwnProperty
57
- && primitive.hasOwnProperty(propName)
58
- );
143
+ openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');
144
+ closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));
145
+ closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));
59
146
  }
60
147
 
61
- // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
62
- // See https://github.com/janl/mustache.js/issues/189
63
- var regExpTest = RegExp.prototype.test;
64
- function testRegExp (re, string) {
65
- return regExpTest.call(re, string);
66
- }
148
+ compileTags(tags || mustache.tags);
67
149
 
68
- var nonSpaceRe = /\S/;
69
- function isWhitespace (string) {
70
- return !testRegExp(nonSpaceRe, string);
71
- }
150
+ var scanner = new Scanner(template);
72
151
 
73
- var entityMap = {
74
- '&': '&amp;',
75
- '<': '&lt;',
76
- '>': '&gt;',
77
- '"': '&quot;',
78
- "'": '&#39;',
79
- '/': '&#x2F;',
80
- '`': '&#x60;',
81
- '=': '&#x3D;'
82
- };
152
+ var start, type, value, chr, token, openSection;
153
+ while (!scanner.eos()) {
154
+ start = scanner.pos;
83
155
 
84
- function escapeHtml (string) {
85
- return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) {
86
- return entityMap[s];
87
- });
88
- }
156
+ // Match any text between tags.
157
+ value = scanner.scanUntil(openingTagRe);
89
158
 
90
- var whiteRe = /\s*/;
91
- var spaceRe = /\s+/;
92
- var equalsRe = /\s*=/;
93
- var curlyRe = /\s*\}/;
94
- var tagRe = /#|\^|\/|>|\{|&|=|!/;
159
+ if (value) {
160
+ for (var i = 0, valueLength = value.length; i < valueLength; ++i) {
161
+ chr = value.charAt(i);
95
162
 
96
- /**
97
- * Breaks up the given `template` string into a tree of tokens. If the `tags`
98
- * argument is given here it must be an array with two string values: the
99
- * opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of
100
- * course, the default is to use mustaches (i.e. mustache.tags).
101
- *
102
- * A token is an array with at least 4 elements. The first element is the
103
- * mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag
104
- * did not contain a symbol (i.e. {{myValue}}) this element is "name". For
105
- * all text that appears outside a symbol this element is "text".
106
- *
107
- * The second element of a token is its "value". For mustache tags this is
108
- * whatever else was inside the tag besides the opening symbol. For text tokens
109
- * this is the text itself.
110
- *
111
- * The third and fourth elements of the token are the start and end indices,
112
- * respectively, of the token in the original template.
113
- *
114
- * Tokens that are the root node of a subtree contain two more elements: 1) an
115
- * array of tokens in the subtree and 2) the index in the original template at
116
- * which the closing tag for that section begins.
117
- *
118
- * Tokens for partials also contain two more elements: 1) a string value of
119
- * indendation prior to that tag and 2) the index of that tag on that line -
120
- * eg a value of 2 indicates the partial is the third tag on this line.
121
- */
122
- function parseTemplate (template, tags) {
123
- if (!template)
124
- return [];
125
- var lineHasNonSpace = false;
126
- var sections = []; // Stack to hold section tokens
127
- var tokens = []; // Buffer to hold the tokens
128
- var spaces = []; // Indices of whitespace tokens on the current line
129
- var hasTag = false; // Is there a {{tag}} on the current line?
130
- var nonSpace = false; // Is there a non-space char on the current line?
131
- var indentation = ''; // Tracks indentation for tags that use it
132
- var tagIndex = 0; // Stores a count of number of tags encountered on a line
133
-
134
- // Strips all whitespace tokens array for the current line
135
- // if there was a {{#tag}} on it and otherwise only space.
136
- function stripSpace () {
137
- if (hasTag && !nonSpace) {
138
- while (spaces.length)
139
- delete tokens[spaces.pop()];
140
- } else {
141
- spaces = [];
142
- }
143
-
144
- hasTag = false;
145
- nonSpace = false;
146
- }
147
-
148
- var openingTagRe, closingTagRe, closingCurlyRe;
149
- function compileTags (tagsToCompile) {
150
- if (typeof tagsToCompile === 'string')
151
- tagsToCompile = tagsToCompile.split(spaceRe, 2);
152
-
153
- if (!isArray(tagsToCompile) || tagsToCompile.length !== 2)
154
- throw new Error('Invalid tags: ' + tagsToCompile);
155
-
156
- openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');
157
- closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));
158
- closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));
159
- }
160
-
161
- compileTags(tags || mustache.tags);
162
-
163
- var scanner = new Scanner(template);
164
-
165
- var start, type, value, chr, token, openSection;
166
- while (!scanner.eos()) {
167
- start = scanner.pos;
168
-
169
- // Match any text between tags.
170
- value = scanner.scanUntil(openingTagRe);
171
-
172
- if (value) {
173
- for (var i = 0, valueLength = value.length; i < valueLength; ++i) {
174
- chr = value.charAt(i);
175
-
176
- if (isWhitespace(chr)) {
177
- spaces.push(tokens.length);
178
- indentation += chr;
179
- } else {
180
- nonSpace = true;
181
- lineHasNonSpace = true;
182
- indentation += ' ';
183
- }
184
-
185
- tokens.push([ 'text', chr, start, start + 1 ]);
186
- start += 1;
187
-
188
- // Check for whitespace on the current line.
189
- if (chr === '\n') {
190
- stripSpace();
191
- indentation = '';
192
- tagIndex = 0;
193
- lineHasNonSpace = false;
194
- }
163
+ if (isWhitespace(chr)) {
164
+ spaces.push(tokens.length);
165
+ indentation += chr;
166
+ } else {
167
+ nonSpace = true;
168
+ lineHasNonSpace = true;
169
+ indentation += ' ';
195
170
  }
196
- }
197
-
198
- // Match the opening tag.
199
- if (!scanner.scan(openingTagRe))
200
- break;
201
-
202
- hasTag = true;
203
-
204
- // Get the tag type.
205
- type = scanner.scan(tagRe) || 'name';
206
- scanner.scan(whiteRe);
207
-
208
- // Get the tag value.
209
- if (type === '=') {
210
- value = scanner.scanUntil(equalsRe);
211
- scanner.scan(equalsRe);
212
- scanner.scanUntil(closingTagRe);
213
- } else if (type === '{') {
214
- value = scanner.scanUntil(closingCurlyRe);
215
- scanner.scan(curlyRe);
216
- scanner.scanUntil(closingTagRe);
217
- type = '&';
218
- } else {
219
- value = scanner.scanUntil(closingTagRe);
220
- }
221
171
 
222
- // Match the closing tag.
223
- if (!scanner.scan(closingTagRe))
224
- throw new Error('Unclosed tag at ' + scanner.pos);
172
+ tokens.push([ 'text', chr, start, start + 1 ]);
173
+ start += 1;
225
174
 
226
- if (type == '>') {
227
- token = [ type, value, start, scanner.pos, indentation, tagIndex, lineHasNonSpace ];
228
- } else {
229
- token = [ type, value, start, scanner.pos ];
230
- }
231
- tagIndex++;
232
- tokens.push(token);
233
-
234
- if (type === '#' || type === '^') {
235
- sections.push(token);
236
- } else if (type === '/') {
237
- // Check section nesting.
238
- openSection = sections.pop();
239
-
240
- if (!openSection)
241
- throw new Error('Unopened section "' + value + '" at ' + start);
242
-
243
- if (openSection[1] !== value)
244
- throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
245
- } else if (type === 'name' || type === '{' || type === '&') {
246
- nonSpace = true;
247
- } else if (type === '=') {
248
- // Set the tags for the next time around.
249
- compileTags(value);
175
+ // Check for whitespace on the current line.
176
+ if (chr === '\n') {
177
+ stripSpace();
178
+ indentation = '';
179
+ tagIndex = 0;
180
+ lineHasNonSpace = false;
181
+ }
250
182
  }
251
183
  }
252
184
 
253
- stripSpace();
254
-
255
- // Make sure there are no open sections when we're done.
256
- openSection = sections.pop();
257
-
258
- if (openSection)
259
- throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
260
-
261
- return nestTokens(squashTokens(tokens));
262
- }
263
-
264
- /**
265
- * Combines the values of consecutive text tokens in the given `tokens` array
266
- * to a single token.
267
- */
268
- function squashTokens (tokens) {
269
- var squashedTokens = [];
270
-
271
- var token, lastToken;
272
- for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
273
- token = tokens[i];
274
-
275
- if (token) {
276
- if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
277
- lastToken[1] += token[1];
278
- lastToken[3] = token[3];
279
- } else {
280
- squashedTokens.push(token);
281
- lastToken = token;
282
- }
283
- }
185
+ // Match the opening tag.
186
+ if (!scanner.scan(openingTagRe))
187
+ break;
188
+
189
+ hasTag = true;
190
+
191
+ // Get the tag type.
192
+ type = scanner.scan(tagRe) || 'name';
193
+ scanner.scan(whiteRe);
194
+
195
+ // Get the tag value.
196
+ if (type === '=') {
197
+ value = scanner.scanUntil(equalsRe);
198
+ scanner.scan(equalsRe);
199
+ scanner.scanUntil(closingTagRe);
200
+ } else if (type === '{') {
201
+ value = scanner.scanUntil(closingCurlyRe);
202
+ scanner.scan(curlyRe);
203
+ scanner.scanUntil(closingTagRe);
204
+ type = '&';
205
+ } else {
206
+ value = scanner.scanUntil(closingTagRe);
284
207
  }
285
208
 
286
- return squashedTokens;
287
- }
209
+ // Match the closing tag.
210
+ if (!scanner.scan(closingTagRe))
211
+ throw new Error('Unclosed tag at ' + scanner.pos);
288
212
 
289
- /**
290
- * Forms the given array of `tokens` into a nested tree structure where
291
- * tokens that represent a section have two additional items: 1) an array of
292
- * all tokens that appear in that section and 2) the index in the original
293
- * template that represents the end of that section.
294
- */
295
- function nestTokens (tokens) {
296
- var nestedTokens = [];
297
- var collector = nestedTokens;
298
- var sections = [];
299
-
300
- var token, section;
301
- for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
302
- token = tokens[i];
303
-
304
- switch (token[0]) {
305
- case '#':
306
- case '^':
307
- collector.push(token);
308
- sections.push(token);
309
- collector = token[4] = [];
310
- break;
311
- case '/':
312
- section = sections.pop();
313
- section[5] = token[2];
314
- collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;
315
- break;
316
- default:
317
- collector.push(token);
318
- }
213
+ if (type == '>') {
214
+ token = [ type, value, start, scanner.pos, indentation, tagIndex, lineHasNonSpace ];
215
+ } else {
216
+ token = [ type, value, start, scanner.pos ];
217
+ }
218
+ tagIndex++;
219
+ tokens.push(token);
220
+
221
+ if (type === '#' || type === '^') {
222
+ sections.push(token);
223
+ } else if (type === '/') {
224
+ // Check section nesting.
225
+ openSection = sections.pop();
226
+
227
+ if (!openSection)
228
+ throw new Error('Unopened section "' + value + '" at ' + start);
229
+
230
+ if (openSection[1] !== value)
231
+ throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
232
+ } else if (type === 'name' || type === '{' || type === '&') {
233
+ nonSpace = true;
234
+ } else if (type === '=') {
235
+ // Set the tags for the next time around.
236
+ compileTags(value);
319
237
  }
320
-
321
- return nestedTokens;
322
238
  }
323
239
 
324
- /**
325
- * A simple string scanner that is used by the template parser to find
326
- * tokens in template strings.
327
- */
328
- function Scanner (string) {
329
- this.string = string;
330
- this.tail = string;
331
- this.pos = 0;
332
- }
240
+ stripSpace();
333
241
 
334
- /**
335
- * Returns `true` if the tail is empty (end of string).
336
- */
337
- Scanner.prototype.eos = function eos () {
338
- return this.tail === '';
339
- };
242
+ // Make sure there are no open sections when we're done.
243
+ openSection = sections.pop();
340
244
 
341
- /**
342
- * Tries to match the given regular expression at the current position.
343
- * Returns the matched text if it can match, the empty string otherwise.
344
- */
345
- Scanner.prototype.scan = function scan (re) {
346
- var match = this.tail.match(re);
245
+ if (openSection)
246
+ throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
347
247
 
348
- if (!match || match.index !== 0)
349
- return '';
248
+ return nestTokens(squashTokens(tokens));
249
+ }
350
250
 
351
- var string = match[0];
251
+ /**
252
+ * Combines the values of consecutive text tokens in the given `tokens` array
253
+ * to a single token.
254
+ */
255
+ function squashTokens (tokens) {
256
+ var squashedTokens = [];
352
257
 
353
- this.tail = this.tail.substring(string.length);
354
- this.pos += string.length;
258
+ var token, lastToken;
259
+ for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
260
+ token = tokens[i];
355
261
 
356
- return string;
357
- };
262
+ if (token) {
263
+ if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
264
+ lastToken[1] += token[1];
265
+ lastToken[3] = token[3];
266
+ } else {
267
+ squashedTokens.push(token);
268
+ lastToken = token;
269
+ }
270
+ }
271
+ }
358
272
 
359
- /**
360
- * Skips all text until the given regular expression can be matched. Returns
361
- * the skipped string, which is the entire tail if no match can be made.
362
- */
363
- Scanner.prototype.scanUntil = function scanUntil (re) {
364
- var index = this.tail.search(re), match;
273
+ return squashedTokens;
274
+ }
365
275
 
366
- switch (index) {
367
- case -1:
368
- match = this.tail;
369
- this.tail = '';
276
+ /**
277
+ * Forms the given array of `tokens` into a nested tree structure where
278
+ * tokens that represent a section have two additional items: 1) an array of
279
+ * all tokens that appear in that section and 2) the index in the original
280
+ * template that represents the end of that section.
281
+ */
282
+ function nestTokens (tokens) {
283
+ var nestedTokens = [];
284
+ var collector = nestedTokens;
285
+ var sections = [];
286
+
287
+ var token, section;
288
+ for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
289
+ token = tokens[i];
290
+
291
+ switch (token[0]) {
292
+ case '#':
293
+ case '^':
294
+ collector.push(token);
295
+ sections.push(token);
296
+ collector = token[4] = [];
370
297
  break;
371
- case 0:
372
- match = '';
298
+ case '/':
299
+ section = sections.pop();
300
+ section[5] = token[2];
301
+ collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;
373
302
  break;
374
303
  default:
375
- match = this.tail.substring(0, index);
376
- this.tail = this.tail.substring(index);
304
+ collector.push(token);
377
305
  }
378
-
379
- this.pos += match.length;
380
-
381
- return match;
382
- };
383
-
384
- /**
385
- * Represents a rendering context by wrapping a view object and
386
- * maintaining a reference to the parent context.
387
- */
388
- function Context (view, parentContext) {
389
- this.view = view;
390
- this.cache = { '.': this.view };
391
- this.parent = parentContext;
392
306
  }
393
307
 
394
- /**
395
- * Creates a new context using the given view with this context
396
- * as the parent.
397
- */
398
- Context.prototype.push = function push (view) {
399
- return new Context(view, this);
400
- };
401
-
402
- /**
403
- * Returns the value of the given name in this context, traversing
404
- * up the context hierarchy if the value is absent in this context's view.
405
- */
406
- Context.prototype.lookup = function lookup (name) {
407
- var cache = this.cache;
308
+ return nestedTokens;
309
+ }
408
310
 
409
- var value;
410
- if (cache.hasOwnProperty(name)) {
411
- value = cache[name];
412
- } else {
413
- var context = this, intermediateValue, names, index, lookupHit = false;
414
-
415
- while (context) {
416
- if (name.indexOf('.') > 0) {
417
- intermediateValue = context.view;
418
- names = name.split('.');
419
- index = 0;
420
-
421
- /**
422
- * Using the dot notion path in `name`, we descend through the
423
- * nested objects.
424
- *
425
- * To be certain that the lookup has been successful, we have to
426
- * check if the last object in the path actually has the property
427
- * we are looking for. We store the result in `lookupHit`.
428
- *
429
- * This is specially necessary for when the value has been set to
430
- * `undefined` and we want to avoid looking up parent contexts.
431
- *
432
- * In the case where dot notation is used, we consider the lookup
433
- * to be successful even if the last "object" in the path is
434
- * not actually an object but a primitive (e.g., a string, or an
435
- * integer), because it is sometimes useful to access a property
436
- * of an autoboxed primitive, such as the length of a string.
437
- **/
438
- while (intermediateValue != null && index < names.length) {
439
- if (index === names.length - 1)
440
- lookupHit = (
441
- hasProperty(intermediateValue, names[index])
442
- || primitiveHasOwnProperty(intermediateValue, names[index])
443
- );
444
-
445
- intermediateValue = intermediateValue[names[index++]];
446
- }
447
- } else {
448
- intermediateValue = context.view[name];
449
-
450
- /**
451
- * Only checking against `hasProperty`, which always returns `false` if
452
- * `context.view` is not an object. Deliberately omitting the check
453
- * against `primitiveHasOwnProperty` if dot notation is not used.
454
- *
455
- * Consider this example:
456
- * ```
457
- * Mustache.render("The length of a football field is {{#length}}{{length}}{{/length}}.", {length: "100 yards"})
458
- * ```
459
- *
460
- * If we were to check also against `primitiveHasOwnProperty`, as we do
461
- * in the dot notation case, then render call would return:
462
- *
463
- * "The length of a football field is 9."
464
- *
465
- * rather than the expected:
466
- *
467
- * "The length of a football field is 100 yards."
468
- **/
469
- lookupHit = hasProperty(context.view, name);
470
- }
311
+ /**
312
+ * A simple string scanner that is used by the template parser to find
313
+ * tokens in template strings.
314
+ */
315
+ function Scanner (string) {
316
+ this.string = string;
317
+ this.tail = string;
318
+ this.pos = 0;
319
+ }
320
+
321
+ /**
322
+ * Returns `true` if the tail is empty (end of string).
323
+ */
324
+ Scanner.prototype.eos = function eos () {
325
+ return this.tail === '';
326
+ };
471
327
 
472
- if (lookupHit) {
473
- value = intermediateValue;
474
- break;
475
- }
328
+ /**
329
+ * Tries to match the given regular expression at the current position.
330
+ * Returns the matched text if it can match, the empty string otherwise.
331
+ */
332
+ Scanner.prototype.scan = function scan (re) {
333
+ var match = this.tail.match(re);
476
334
 
477
- context = context.parent;
478
- }
335
+ if (!match || match.index !== 0)
336
+ return '';
479
337
 
480
- cache[name] = value;
481
- }
338
+ var string = match[0];
482
339
 
483
- if (isFunction(value))
484
- value = value.call(this.view);
340
+ this.tail = this.tail.substring(string.length);
341
+ this.pos += string.length;
485
342
 
486
- return value;
487
- };
343
+ return string;
344
+ };
488
345
 
489
- /**
490
- * A Writer knows how to take a stream of tokens and render them to a
491
- * string, given a context. It also maintains a cache of templates to
492
- * avoid the need to parse the same template twice.
493
- */
494
- function Writer () {
495
- this.cache = {};
346
+ /**
347
+ * Skips all text until the given regular expression can be matched. Returns
348
+ * the skipped string, which is the entire tail if no match can be made.
349
+ */
350
+ Scanner.prototype.scanUntil = function scanUntil (re) {
351
+ var index = this.tail.search(re), match;
352
+
353
+ switch (index) {
354
+ case -1:
355
+ match = this.tail;
356
+ this.tail = '';
357
+ break;
358
+ case 0:
359
+ match = '';
360
+ break;
361
+ default:
362
+ match = this.tail.substring(0, index);
363
+ this.tail = this.tail.substring(index);
496
364
  }
497
365
 
498
- /**
499
- * Clears all cached templates in this writer.
500
- */
501
- Writer.prototype.clearCache = function clearCache () {
502
- this.cache = {};
503
- };
504
-
505
- /**
506
- * Parses and caches the given `template` according to the given `tags` or
507
- * `mustache.tags` if `tags` is omitted, and returns the array of tokens
508
- * that is generated from the parse.
509
- */
510
- Writer.prototype.parse = function parse (template, tags) {
511
- var cache = this.cache;
512
- var cacheKey = template + ':' + (tags || mustache.tags).join(':');
513
- var tokens = cache[cacheKey];
514
-
515
- if (tokens == null)
516
- tokens = cache[cacheKey] = parseTemplate(template, tags);
366
+ this.pos += match.length;
517
367
 
518
- return tokens;
519
- };
368
+ return match;
369
+ };
520
370
 
521
- /**
522
- * High-level method that is used to render the given `template` with
523
- * the given `view`.
524
- *
525
- * The optional `partials` argument may be an object that contains the
526
- * names and templates of partials that are used in the template. It may
527
- * also be a function that is used to load partial templates on the fly
528
- * that takes a single argument: the name of the partial.
529
- *
530
- * If the optional `tags` argument is given here it must be an array with two
531
- * string values: the opening and closing tags used in the template (e.g.
532
- * [ "<%", "%>" ]). The default is to mustache.tags.
533
- */
534
- Writer.prototype.render = function render (template, view, partials, tags) {
535
- var tokens = this.parse(template, tags);
536
- var context = (view instanceof Context) ? view : new Context(view);
537
- return this.renderTokens(tokens, context, partials, template, tags);
538
- };
371
+ /**
372
+ * Represents a rendering context by wrapping a view object and
373
+ * maintaining a reference to the parent context.
374
+ */
375
+ function Context (view, parentContext) {
376
+ this.view = view;
377
+ this.cache = { '.': this.view };
378
+ this.parent = parentContext;
379
+ }
380
+
381
+ /**
382
+ * Creates a new context using the given view with this context
383
+ * as the parent.
384
+ */
385
+ Context.prototype.push = function push (view) {
386
+ return new Context(view, this);
387
+ };
539
388
 
540
- /**
541
- * Low-level method that renders the given array of `tokens` using
542
- * the given `context` and `partials`.
543
- *
544
- * Note: The `originalTemplate` is only ever used to extract the portion
545
- * of the original template that was contained in a higher-order section.
546
- * If the template doesn't use higher-order sections, this argument may
547
- * be omitted.
548
- */
549
- Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate, tags) {
550
- var buffer = '';
551
-
552
- var token, symbol, value;
553
- for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
554
- value = undefined;
555
- token = tokens[i];
556
- symbol = token[0];
557
-
558
- if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate);
559
- else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate);
560
- else if (symbol === '>') value = this.renderPartial(token, context, partials, tags);
561
- else if (symbol === '&') value = this.unescapedValue(token, context);
562
- else if (symbol === 'name') value = this.escapedValue(token, context);
563
- else if (symbol === 'text') value = this.rawValue(token);
564
-
565
- if (value !== undefined)
566
- buffer += value;
567
- }
389
+ /**
390
+ * Returns the value of the given name in this context, traversing
391
+ * up the context hierarchy if the value is absent in this context's view.
392
+ */
393
+ Context.prototype.lookup = function lookup (name) {
394
+ var cache = this.cache;
568
395
 
569
- return buffer;
570
- };
396
+ var value;
397
+ if (cache.hasOwnProperty(name)) {
398
+ value = cache[name];
399
+ } else {
400
+ var context = this, intermediateValue, names, index, lookupHit = false;
401
+
402
+ while (context) {
403
+ if (name.indexOf('.') > 0) {
404
+ intermediateValue = context.view;
405
+ names = name.split('.');
406
+ index = 0;
407
+
408
+ /**
409
+ * Using the dot notion path in `name`, we descend through the
410
+ * nested objects.
411
+ *
412
+ * To be certain that the lookup has been successful, we have to
413
+ * check if the last object in the path actually has the property
414
+ * we are looking for. We store the result in `lookupHit`.
415
+ *
416
+ * This is specially necessary for when the value has been set to
417
+ * `undefined` and we want to avoid looking up parent contexts.
418
+ *
419
+ * In the case where dot notation is used, we consider the lookup
420
+ * to be successful even if the last "object" in the path is
421
+ * not actually an object but a primitive (e.g., a string, or an
422
+ * integer), because it is sometimes useful to access a property
423
+ * of an autoboxed primitive, such as the length of a string.
424
+ **/
425
+ while (intermediateValue != null && index < names.length) {
426
+ if (index === names.length - 1)
427
+ lookupHit = (
428
+ hasProperty(intermediateValue, names[index])
429
+ || primitiveHasOwnProperty(intermediateValue, names[index])
430
+ );
431
+
432
+ intermediateValue = intermediateValue[names[index++]];
433
+ }
434
+ } else {
435
+ intermediateValue = context.view[name];
436
+
437
+ /**
438
+ * Only checking against `hasProperty`, which always returns `false` if
439
+ * `context.view` is not an object. Deliberately omitting the check
440
+ * against `primitiveHasOwnProperty` if dot notation is not used.
441
+ *
442
+ * Consider this example:
443
+ * ```
444
+ * Mustache.render("The length of a football field is {{#length}}{{length}}{{/length}}.", {length: "100 yards"})
445
+ * ```
446
+ *
447
+ * If we were to check also against `primitiveHasOwnProperty`, as we do
448
+ * in the dot notation case, then render call would return:
449
+ *
450
+ * "The length of a football field is 9."
451
+ *
452
+ * rather than the expected:
453
+ *
454
+ * "The length of a football field is 100 yards."
455
+ **/
456
+ lookupHit = hasProperty(context.view, name);
457
+ }
571
458
 
572
- Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) {
573
- var self = this;
574
- var buffer = '';
575
- var value = context.lookup(token[1]);
459
+ if (lookupHit) {
460
+ value = intermediateValue;
461
+ break;
462
+ }
576
463
 
577
- // This function is used to render an arbitrary template
578
- // in the current context by higher-order sections.
579
- function subRender (template) {
580
- return self.render(template, context, partials);
464
+ context = context.parent;
581
465
  }
582
466
 
583
- if (!value) return;
467
+ cache[name] = value;
468
+ }
584
469
 
585
- if (isArray(value)) {
586
- for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
587
- buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate);
588
- }
589
- } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {
590
- buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate);
591
- } else if (isFunction(value)) {
592
- if (typeof originalTemplate !== 'string')
593
- throw new Error('Cannot use higher-order sections without the original template');
470
+ if (isFunction(value))
471
+ value = value.call(this.view);
594
472
 
595
- // Extract the portion of the original template that the section contains.
596
- value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);
473
+ return value;
474
+ };
597
475
 
598
- if (value != null)
599
- buffer += value;
600
- } else {
601
- buffer += this.renderTokens(token[4], context, partials, originalTemplate);
476
+ /**
477
+ * A Writer knows how to take a stream of tokens and render them to a
478
+ * string, given a context. It also maintains a cache of templates to
479
+ * avoid the need to parse the same template twice.
480
+ */
481
+ function Writer () {
482
+ this.templateCache = {
483
+ _cache: {},
484
+ set: function set (key, value) {
485
+ this._cache[key] = value;
486
+ },
487
+ get: function get (key) {
488
+ return this._cache[key];
489
+ },
490
+ clear: function clear () {
491
+ this._cache = {};
602
492
  }
603
- return buffer;
604
493
  };
494
+ }
605
495
 
606
- Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) {
607
- var value = context.lookup(token[1]);
496
+ /**
497
+ * Clears all cached templates in this writer.
498
+ */
499
+ Writer.prototype.clearCache = function clearCache () {
500
+ if (typeof this.templateCache !== 'undefined') {
501
+ this.templateCache.clear();
502
+ }
503
+ };
608
504
 
609
- // Use JavaScript's definition of falsy. Include empty arrays.
610
- // See https://github.com/janl/mustache.js/issues/186
611
- if (!value || (isArray(value) && value.length === 0))
612
- return this.renderTokens(token[4], context, partials, originalTemplate);
613
- };
505
+ /**
506
+ * Parses and caches the given `template` according to the given `tags` or
507
+ * `mustache.tags` if `tags` is omitted, and returns the array of tokens
508
+ * that is generated from the parse.
509
+ */
510
+ Writer.prototype.parse = function parse (template, tags) {
511
+ var cache = this.templateCache;
512
+ var cacheKey = template + ':' + (tags || mustache.tags).join(':');
513
+ var isCacheEnabled = typeof cache !== 'undefined';
514
+ var tokens = isCacheEnabled ? cache.get(cacheKey) : undefined;
515
+
516
+ if (tokens == undefined) {
517
+ tokens = parseTemplate(template, tags);
518
+ isCacheEnabled && cache.set(cacheKey, tokens);
519
+ }
520
+ return tokens;
521
+ };
522
+
523
+ /**
524
+ * High-level method that is used to render the given `template` with
525
+ * the given `view`.
526
+ *
527
+ * The optional `partials` argument may be an object that contains the
528
+ * names and templates of partials that are used in the template. It may
529
+ * also be a function that is used to load partial templates on the fly
530
+ * that takes a single argument: the name of the partial.
531
+ *
532
+ * If the optional `config` argument is given here, then it should be an
533
+ * object with a `tags` attribute or an `escape` attribute or both.
534
+ * If an array is passed, then it will be interpreted the same way as
535
+ * a `tags` attribute on a `config` object.
536
+ *
537
+ * The `tags` attribute of a `config` object must be an array with two
538
+ * string values: the opening and closing tags used in the template (e.g.
539
+ * [ "<%", "%>" ]). The default is to mustache.tags.
540
+ *
541
+ * The `escape` attribute of a `config` object must be a function which
542
+ * accepts a string as input and outputs a safely escaped string.
543
+ * If an `escape` function is not provided, then an HTML-safe string
544
+ * escaping function is used as the default.
545
+ */
546
+ Writer.prototype.render = function render (template, view, partials, config) {
547
+ var tags = this.getConfigTags(config);
548
+ var tokens = this.parse(template, tags);
549
+ var context = (view instanceof Context) ? view : new Context(view, undefined);
550
+ return this.renderTokens(tokens, context, partials, template, config);
551
+ };
552
+
553
+ /**
554
+ * Low-level method that renders the given array of `tokens` using
555
+ * the given `context` and `partials`.
556
+ *
557
+ * Note: The `originalTemplate` is only ever used to extract the portion
558
+ * of the original template that was contained in a higher-order section.
559
+ * If the template doesn't use higher-order sections, this argument may
560
+ * be omitted.
561
+ */
562
+ Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate, config) {
563
+ var buffer = '';
564
+
565
+ var token, symbol, value;
566
+ for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
567
+ value = undefined;
568
+ token = tokens[i];
569
+ symbol = token[0];
570
+
571
+ if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate, config);
572
+ else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate, config);
573
+ else if (symbol === '>') value = this.renderPartial(token, context, partials, config);
574
+ else if (symbol === '&') value = this.unescapedValue(token, context);
575
+ else if (symbol === 'name') value = this.escapedValue(token, context, config);
576
+ else if (symbol === 'text') value = this.rawValue(token);
577
+
578
+ if (value !== undefined)
579
+ buffer += value;
580
+ }
614
581
 
615
- Writer.prototype.indentPartial = function indentPartial (partial, indentation, lineHasNonSpace) {
616
- var filteredIndentation = indentation.replace(/[^ \t]/g, '');
617
- var partialByNl = partial.split('\n');
618
- for (var i = 0; i < partialByNl.length; i++) {
619
- if (partialByNl[i].length && (i > 0 || !lineHasNonSpace)) {
620
- partialByNl[i] = filteredIndentation + partialByNl[i];
621
- }
622
- }
623
- return partialByNl.join('\n');
624
- };
582
+ return buffer;
583
+ };
625
584
 
626
- Writer.prototype.renderPartial = function renderPartial (token, context, partials, tags) {
627
- if (!partials) return;
628
-
629
- var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
630
- if (value != null) {
631
- var lineHasNonSpace = token[6];
632
- var tagIndex = token[5];
633
- var indentation = token[4];
634
- var indentedValue = value;
635
- if (tagIndex == 0 && indentation) {
636
- indentedValue = this.indentPartial(value, indentation, lineHasNonSpace);
637
- }
638
- return this.renderTokens(this.parse(indentedValue, tags), context, partials, indentedValue);
639
- }
640
- };
585
+ Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate, config) {
586
+ var self = this;
587
+ var buffer = '';
588
+ var value = context.lookup(token[1]);
641
589
 
642
- Writer.prototype.unescapedValue = function unescapedValue (token, context) {
643
- var value = context.lookup(token[1]);
644
- if (value != null)
645
- return value;
646
- };
590
+ // This function is used to render an arbitrary template
591
+ // in the current context by higher-order sections.
592
+ function subRender (template) {
593
+ return self.render(template, context, partials, config);
594
+ }
647
595
 
648
- Writer.prototype.escapedValue = function escapedValue (token, context) {
649
- var value = context.lookup(token[1]);
650
- if (value != null)
651
- return mustache.escape(value);
652
- };
596
+ if (!value) return;
653
597
 
654
- Writer.prototype.rawValue = function rawValue (token) {
655
- return token[1];
656
- };
598
+ if (isArray(value)) {
599
+ for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
600
+ buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate, config);
601
+ }
602
+ } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {
603
+ buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate, config);
604
+ } else if (isFunction(value)) {
605
+ if (typeof originalTemplate !== 'string')
606
+ throw new Error('Cannot use higher-order sections without the original template');
657
607
 
658
- mustache.name = 'mustache.js';
659
- mustache.version = '3.1.0';
660
- mustache.tags = [ '{{', '}}' ];
608
+ // Extract the portion of the original template that the section contains.
609
+ value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);
661
610
 
662
- // All high-level mustache.* functions use this writer.
663
- var defaultWriter = new Writer();
611
+ if (value != null)
612
+ buffer += value;
613
+ } else {
614
+ buffer += this.renderTokens(token[4], context, partials, originalTemplate, config);
615
+ }
616
+ return buffer;
617
+ };
618
+
619
+ Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate, config) {
620
+ var value = context.lookup(token[1]);
621
+
622
+ // Use JavaScript's definition of falsy. Include empty arrays.
623
+ // See https://github.com/janl/mustache.js/issues/186
624
+ if (!value || (isArray(value) && value.length === 0))
625
+ return this.renderTokens(token[4], context, partials, originalTemplate, config);
626
+ };
627
+
628
+ Writer.prototype.indentPartial = function indentPartial (partial, indentation, lineHasNonSpace) {
629
+ var filteredIndentation = indentation.replace(/[^ \t]/g, '');
630
+ var partialByNl = partial.split('\n');
631
+ for (var i = 0; i < partialByNl.length; i++) {
632
+ if (partialByNl[i].length && (i > 0 || !lineHasNonSpace)) {
633
+ partialByNl[i] = filteredIndentation + partialByNl[i];
634
+ }
635
+ }
636
+ return partialByNl.join('\n');
637
+ };
638
+
639
+ Writer.prototype.renderPartial = function renderPartial (token, context, partials, config) {
640
+ if (!partials) return;
641
+ var tags = this.getConfigTags(config);
642
+
643
+ var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
644
+ if (value != null) {
645
+ var lineHasNonSpace = token[6];
646
+ var tagIndex = token[5];
647
+ var indentation = token[4];
648
+ var indentedValue = value;
649
+ if (tagIndex == 0 && indentation) {
650
+ indentedValue = this.indentPartial(value, indentation, lineHasNonSpace);
651
+ }
652
+ var tokens = this.parse(indentedValue, tags);
653
+ return this.renderTokens(tokens, context, partials, indentedValue, config);
654
+ }
655
+ };
664
656
 
665
- /**
666
- * Clears all cached templates in the default writer.
667
- */
668
- mustache.clearCache = function clearCache () {
669
- return defaultWriter.clearCache();
670
- };
657
+ Writer.prototype.unescapedValue = function unescapedValue (token, context) {
658
+ var value = context.lookup(token[1]);
659
+ if (value != null)
660
+ return value;
661
+ };
662
+
663
+ Writer.prototype.escapedValue = function escapedValue (token, context, config) {
664
+ var escape = this.getConfigEscape(config) || mustache.escape;
665
+ var value = context.lookup(token[1]);
666
+ if (value != null)
667
+ return (typeof value === 'number' && escape === mustache.escape) ? String(value) : escape(value);
668
+ };
669
+
670
+ Writer.prototype.rawValue = function rawValue (token) {
671
+ return token[1];
672
+ };
673
+
674
+ Writer.prototype.getConfigTags = function getConfigTags (config) {
675
+ if (isArray(config)) {
676
+ return config;
677
+ }
678
+ else if (config && typeof config === 'object') {
679
+ return config.tags;
680
+ }
681
+ else {
682
+ return undefined;
683
+ }
684
+ };
671
685
 
686
+ Writer.prototype.getConfigEscape = function getConfigEscape (config) {
687
+ if (config && typeof config === 'object' && !isArray(config)) {
688
+ return config.escape;
689
+ }
690
+ else {
691
+ return undefined;
692
+ }
693
+ };
694
+
695
+ var mustache = {
696
+ name: 'mustache.js',
697
+ version: '4.2.0',
698
+ tags: [ '{{', '}}' ],
699
+ clearCache: undefined,
700
+ escape: undefined,
701
+ parse: undefined,
702
+ render: undefined,
703
+ Scanner: undefined,
704
+ Context: undefined,
705
+ Writer: undefined,
672
706
  /**
673
- * Parses and caches the given template in the default writer and returns the
674
- * array of tokens it contains. Doing this ahead of time avoids the need to
675
- * parse templates on the fly as they are rendered.
707
+ * Allows a user to override the default caching strategy, by providing an
708
+ * object with set, get and clear methods. This can also be used to disable
709
+ * the cache by setting it to the literal `undefined`.
676
710
  */
677
- mustache.parse = function parse (template, tags) {
678
- return defaultWriter.parse(template, tags);
679
- };
680
-
711
+ set templateCache (cache) {
712
+ defaultWriter.templateCache = cache;
713
+ },
681
714
  /**
682
- * Renders the `template` with the given `view` and `partials` using the
683
- * default writer. If the optional `tags` argument is given here it must be an
684
- * array with two string values: the opening and closing tags used in the
685
- * template (e.g. [ "<%", "%>" ]). The default is to mustache.tags.
715
+ * Gets the default or overridden caching object from the default writer.
686
716
  */
687
- mustache.render = function render (template, view, partials, tags) {
688
- if (typeof template !== 'string') {
689
- throw new TypeError('Invalid template! Template should be a "string" ' +
690
- 'but "' + typeStr(template) + '" was given as the first ' +
691
- 'argument for mustache#render(template, view, partials)');
692
- }
717
+ get templateCache () {
718
+ return defaultWriter.templateCache;
719
+ }
720
+ };
693
721
 
694
- return defaultWriter.render(template, view, partials, tags);
695
- };
722
+ // All high-level mustache.* functions use this writer.
723
+ var defaultWriter = new Writer();
696
724
 
697
- // This is here for backwards compatibility with 0.4.x.,
698
- /*eslint-disable */ // eslint wants camel cased function name
699
- mustache.to_html = function to_html (template, view, partials, send) {
700
- /*eslint-enable*/
725
+ /**
726
+ * Clears all cached templates in the default writer.
727
+ */
728
+ mustache.clearCache = function clearCache () {
729
+ return defaultWriter.clearCache();
730
+ };
731
+
732
+ /**
733
+ * Parses and caches the given template in the default writer and returns the
734
+ * array of tokens it contains. Doing this ahead of time avoids the need to
735
+ * parse templates on the fly as they are rendered.
736
+ */
737
+ mustache.parse = function parse (template, tags) {
738
+ return defaultWriter.parse(template, tags);
739
+ };
701
740
 
702
- var result = mustache.render(template, view, partials);
741
+ /**
742
+ * Renders the `template` with the given `view`, `partials`, and `config`
743
+ * using the default writer.
744
+ */
745
+ mustache.render = function render (template, view, partials, config) {
746
+ if (typeof template !== 'string') {
747
+ throw new TypeError('Invalid template! Template should be a "string" ' +
748
+ 'but "' + typeStr(template) + '" was given as the first ' +
749
+ 'argument for mustache#render(template, view, partials)');
750
+ }
703
751
 
704
- if (isFunction(send)) {
705
- send(result);
706
- } else {
707
- return result;
708
- }
709
- };
752
+ return defaultWriter.render(template, view, partials, config);
753
+ };
710
754
 
711
- // Export the escaping function so that the user may override it.
712
- // See https://github.com/janl/mustache.js/issues/244
713
- mustache.escape = escapeHtml;
755
+ // Export the escaping function so that the user may override it.
756
+ // See https://github.com/janl/mustache.js/issues/244
757
+ mustache.escape = escapeHtml;
714
758
 
715
- // Export these mainly for testing, but also for advanced usage.
716
- mustache.Scanner = Scanner;
717
- mustache.Context = Context;
718
- mustache.Writer = Writer;
759
+ // Export these mainly for testing, but also for advanced usage.
760
+ mustache.Scanner = Scanner;
761
+ mustache.Context = Context;
762
+ mustache.Writer = Writer;
719
763
 
720
- return mustache;
721
- }));
764
+ export default mustache;