1 | (function (global, factory) {
|
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
3 | typeof define === 'function' && define.amd ? define(factory) :
|
4 | (global = global || self, global.Mustache = factory());
|
5 | }(this, (function () { 'use strict';
|
6 |
|
7 | |
8 |
|
9 |
|
10 |
|
11 |
|
12 | var objectToString = Object.prototype.toString;
|
13 | var isArray = Array.isArray || function isArrayPolyfill (object) {
|
14 | return objectToString.call(object) === '[object Array]';
|
15 | };
|
16 |
|
17 | function isFunction (object) {
|
18 | return typeof object === 'function';
|
19 | }
|
20 |
|
21 | |
22 |
|
23 |
|
24 |
|
25 | function typeStr (obj) {
|
26 | return isArray(obj) ? 'array' : typeof obj;
|
27 | }
|
28 |
|
29 | function escapeRegExp (string) {
|
30 | return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
|
31 | }
|
32 |
|
33 | |
34 |
|
35 |
|
36 |
|
37 | function hasProperty (obj, propName) {
|
38 | return obj != null && typeof obj === 'object' && (propName in obj);
|
39 | }
|
40 |
|
41 | |
42 |
|
43 |
|
44 |
|
45 | function primitiveHasOwnProperty (primitive, propName) {
|
46 | return (
|
47 | primitive != null
|
48 | && typeof primitive !== 'object'
|
49 | && primitive.hasOwnProperty
|
50 | && primitive.hasOwnProperty(propName)
|
51 | );
|
52 | }
|
53 |
|
54 |
|
55 |
|
56 | var regExpTest = RegExp.prototype.test;
|
57 | function testRegExp (re, string) {
|
58 | return regExpTest.call(re, string);
|
59 | }
|
60 |
|
61 | var nonSpaceRe = /\S/;
|
62 | function isWhitespace (string) {
|
63 | return !testRegExp(nonSpaceRe, string);
|
64 | }
|
65 |
|
66 | var entityMap = {
|
67 | '&': '&',
|
68 | '<': '<',
|
69 | '>': '>',
|
70 | '"': '"',
|
71 | "'": ''',
|
72 | '/': '/',
|
73 | '`': '`',
|
74 | '=': '='
|
75 | };
|
76 |
|
77 | function escapeHtml (string) {
|
78 | return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) {
|
79 | return entityMap[s];
|
80 | });
|
81 | }
|
82 |
|
83 | var whiteRe = /\s*/;
|
84 | var spaceRe = /\s+/;
|
85 | var equalsRe = /\s*=/;
|
86 | var curlyRe = /\s*\}/;
|
87 | var tagRe = /#|\^|\/|>|\{|&|=|!/;
|
88 |
|
89 | |
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 | function parseTemplate (template, tags) {
|
116 | if (!template)
|
117 | return [];
|
118 | var lineHasNonSpace = false;
|
119 | var sections = [];
|
120 | var tokens = [];
|
121 | var spaces = [];
|
122 | var hasTag = false;
|
123 | var nonSpace = false;
|
124 | var indentation = '';
|
125 | var tagIndex = 0;
|
126 |
|
127 |
|
128 |
|
129 | function stripSpace () {
|
130 | if (hasTag && !nonSpace) {
|
131 | while (spaces.length)
|
132 | delete tokens[spaces.pop()];
|
133 | } else {
|
134 | spaces = [];
|
135 | }
|
136 |
|
137 | hasTag = false;
|
138 | nonSpace = false;
|
139 | }
|
140 |
|
141 | var openingTagRe, closingTagRe, closingCurlyRe;
|
142 | function compileTags (tagsToCompile) {
|
143 | if (typeof tagsToCompile === 'string')
|
144 | tagsToCompile = tagsToCompile.split(spaceRe, 2);
|
145 |
|
146 | if (!isArray(tagsToCompile) || tagsToCompile.length !== 2)
|
147 | throw new Error('Invalid tags: ' + tagsToCompile);
|
148 |
|
149 | openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');
|
150 | closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));
|
151 | closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));
|
152 | }
|
153 |
|
154 | compileTags(tags || mustache.tags);
|
155 |
|
156 | var scanner = new Scanner(template);
|
157 |
|
158 | var start, type, value, chr, token, openSection;
|
159 | while (!scanner.eos()) {
|
160 | start = scanner.pos;
|
161 |
|
162 |
|
163 | value = scanner.scanUntil(openingTagRe);
|
164 |
|
165 | if (value) {
|
166 | for (var i = 0, valueLength = value.length; i < valueLength; ++i) {
|
167 | chr = value.charAt(i);
|
168 |
|
169 | if (isWhitespace(chr)) {
|
170 | spaces.push(tokens.length);
|
171 | indentation += chr;
|
172 | } else {
|
173 | nonSpace = true;
|
174 | lineHasNonSpace = true;
|
175 | indentation += ' ';
|
176 | }
|
177 |
|
178 | tokens.push([ 'text', chr, start, start + 1 ]);
|
179 | start += 1;
|
180 |
|
181 |
|
182 | if (chr === '\n') {
|
183 | stripSpace();
|
184 | indentation = '';
|
185 | tagIndex = 0;
|
186 | lineHasNonSpace = false;
|
187 | }
|
188 | }
|
189 | }
|
190 |
|
191 |
|
192 | if (!scanner.scan(openingTagRe))
|
193 | break;
|
194 |
|
195 | hasTag = true;
|
196 |
|
197 |
|
198 | type = scanner.scan(tagRe) || 'name';
|
199 | scanner.scan(whiteRe);
|
200 |
|
201 |
|
202 | if (type === '=') {
|
203 | value = scanner.scanUntil(equalsRe);
|
204 | scanner.scan(equalsRe);
|
205 | scanner.scanUntil(closingTagRe);
|
206 | } else if (type === '{') {
|
207 | value = scanner.scanUntil(closingCurlyRe);
|
208 | scanner.scan(curlyRe);
|
209 | scanner.scanUntil(closingTagRe);
|
210 | type = '&';
|
211 | } else {
|
212 | value = scanner.scanUntil(closingTagRe);
|
213 | }
|
214 |
|
215 |
|
216 | if (!scanner.scan(closingTagRe))
|
217 | throw new Error('Unclosed tag at ' + scanner.pos);
|
218 |
|
219 | if (type == '>') {
|
220 | token = [ type, value, start, scanner.pos, indentation, tagIndex, lineHasNonSpace ];
|
221 | } else {
|
222 | token = [ type, value, start, scanner.pos ];
|
223 | }
|
224 | tagIndex++;
|
225 | tokens.push(token);
|
226 |
|
227 | if (type === '#' || type === '^') {
|
228 | sections.push(token);
|
229 | } else if (type === '/') {
|
230 |
|
231 | openSection = sections.pop();
|
232 |
|
233 | if (!openSection)
|
234 | throw new Error('Unopened section "' + value + '" at ' + start);
|
235 |
|
236 | if (openSection[1] !== value)
|
237 | throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
|
238 | } else if (type === 'name' || type === '{' || type === '&') {
|
239 | nonSpace = true;
|
240 | } else if (type === '=') {
|
241 |
|
242 | compileTags(value);
|
243 | }
|
244 | }
|
245 |
|
246 | stripSpace();
|
247 |
|
248 |
|
249 | openSection = sections.pop();
|
250 |
|
251 | if (openSection)
|
252 | throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
|
253 |
|
254 | return nestTokens(squashTokens(tokens));
|
255 | }
|
256 |
|
257 | |
258 |
|
259 |
|
260 |
|
261 | function squashTokens (tokens) {
|
262 | var squashedTokens = [];
|
263 |
|
264 | var token, lastToken;
|
265 | for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
266 | token = tokens[i];
|
267 |
|
268 | if (token) {
|
269 | if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
|
270 | lastToken[1] += token[1];
|
271 | lastToken[3] = token[3];
|
272 | } else {
|
273 | squashedTokens.push(token);
|
274 | lastToken = token;
|
275 | }
|
276 | }
|
277 | }
|
278 |
|
279 | return squashedTokens;
|
280 | }
|
281 |
|
282 | |
283 |
|
284 |
|
285 |
|
286 |
|
287 |
|
288 | function nestTokens (tokens) {
|
289 | var nestedTokens = [];
|
290 | var collector = nestedTokens;
|
291 | var sections = [];
|
292 |
|
293 | var token, section;
|
294 | for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
295 | token = tokens[i];
|
296 |
|
297 | switch (token[0]) {
|
298 | case '#':
|
299 | case '^':
|
300 | collector.push(token);
|
301 | sections.push(token);
|
302 | collector = token[4] = [];
|
303 | break;
|
304 | case '/':
|
305 | section = sections.pop();
|
306 | section[5] = token[2];
|
307 | collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;
|
308 | break;
|
309 | default:
|
310 | collector.push(token);
|
311 | }
|
312 | }
|
313 |
|
314 | return nestedTokens;
|
315 | }
|
316 |
|
317 | |
318 |
|
319 |
|
320 |
|
321 | function Scanner (string) {
|
322 | this.string = string;
|
323 | this.tail = string;
|
324 | this.pos = 0;
|
325 | }
|
326 |
|
327 | |
328 |
|
329 |
|
330 | Scanner.prototype.eos = function eos () {
|
331 | return this.tail === '';
|
332 | };
|
333 |
|
334 | |
335 |
|
336 |
|
337 |
|
338 | Scanner.prototype.scan = function scan (re) {
|
339 | var match = this.tail.match(re);
|
340 |
|
341 | if (!match || match.index !== 0)
|
342 | return '';
|
343 |
|
344 | var string = match[0];
|
345 |
|
346 | this.tail = this.tail.substring(string.length);
|
347 | this.pos += string.length;
|
348 |
|
349 | return string;
|
350 | };
|
351 |
|
352 | |
353 |
|
354 |
|
355 |
|
356 | Scanner.prototype.scanUntil = function scanUntil (re) {
|
357 | var index = this.tail.search(re), match;
|
358 |
|
359 | switch (index) {
|
360 | case -1:
|
361 | match = this.tail;
|
362 | this.tail = '';
|
363 | break;
|
364 | case 0:
|
365 | match = '';
|
366 | break;
|
367 | default:
|
368 | match = this.tail.substring(0, index);
|
369 | this.tail = this.tail.substring(index);
|
370 | }
|
371 |
|
372 | this.pos += match.length;
|
373 |
|
374 | return match;
|
375 | };
|
376 |
|
377 | |
378 |
|
379 |
|
380 |
|
381 | function Context (view, parentContext) {
|
382 | this.view = view;
|
383 | this.cache = { '.': this.view };
|
384 | this.parent = parentContext;
|
385 | }
|
386 |
|
387 | |
388 |
|
389 |
|
390 |
|
391 | Context.prototype.push = function push (view) {
|
392 | return new Context(view, this);
|
393 | };
|
394 |
|
395 | |
396 |
|
397 |
|
398 |
|
399 | Context.prototype.lookup = function lookup (name) {
|
400 | var cache = this.cache;
|
401 |
|
402 | var value;
|
403 | if (cache.hasOwnProperty(name)) {
|
404 | value = cache[name];
|
405 | } else {
|
406 | var context = this, intermediateValue, names, index, lookupHit = false;
|
407 |
|
408 | while (context) {
|
409 | if (name.indexOf('.') > 0) {
|
410 | intermediateValue = context.view;
|
411 | names = name.split('.');
|
412 | index = 0;
|
413 |
|
414 | |
415 |
|
416 |
|
417 |
|
418 |
|
419 |
|
420 |
|
421 |
|
422 |
|
423 |
|
424 |
|
425 |
|
426 |
|
427 |
|
428 |
|
429 |
|
430 |
|
431 | while (intermediateValue != null && index < names.length) {
|
432 | if (index === names.length - 1)
|
433 | lookupHit = (
|
434 | hasProperty(intermediateValue, names[index])
|
435 | || primitiveHasOwnProperty(intermediateValue, names[index])
|
436 | );
|
437 |
|
438 | intermediateValue = intermediateValue[names[index++]];
|
439 | }
|
440 | } else {
|
441 | intermediateValue = context.view[name];
|
442 |
|
443 | |
444 |
|
445 |
|
446 |
|
447 |
|
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 |
|
454 |
|
455 |
|
456 |
|
457 |
|
458 |
|
459 |
|
460 |
|
461 |
|
462 | lookupHit = hasProperty(context.view, name);
|
463 | }
|
464 |
|
465 | if (lookupHit) {
|
466 | value = intermediateValue;
|
467 | break;
|
468 | }
|
469 |
|
470 | context = context.parent;
|
471 | }
|
472 |
|
473 | cache[name] = value;
|
474 | }
|
475 |
|
476 | if (isFunction(value))
|
477 | value = value.call(this.view);
|
478 |
|
479 | return value;
|
480 | };
|
481 |
|
482 | |
483 |
|
484 |
|
485 |
|
486 |
|
487 | function Writer () {
|
488 | this.templateCache = {
|
489 | _cache: {},
|
490 | set: function set (key, value) {
|
491 | this._cache[key] = value;
|
492 | },
|
493 | get: function get (key) {
|
494 | return this._cache[key];
|
495 | },
|
496 | clear: function clear () {
|
497 | this._cache = {};
|
498 | }
|
499 | };
|
500 | }
|
501 |
|
502 | |
503 |
|
504 |
|
505 | Writer.prototype.clearCache = function clearCache () {
|
506 | if (typeof this.templateCache !== 'undefined') {
|
507 | this.templateCache.clear();
|
508 | }
|
509 | };
|
510 |
|
511 | |
512 |
|
513 |
|
514 |
|
515 |
|
516 | Writer.prototype.parse = function parse (template, tags) {
|
517 | var cache = this.templateCache;
|
518 | var cacheKey = template + ':' + (tags || mustache.tags).join(':');
|
519 | var isCacheEnabled = typeof cache !== 'undefined';
|
520 | var tokens = isCacheEnabled ? cache.get(cacheKey) : undefined;
|
521 |
|
522 | if (tokens == undefined) {
|
523 | tokens = parseTemplate(template, tags);
|
524 | isCacheEnabled && cache.set(cacheKey, tokens);
|
525 | }
|
526 | return tokens;
|
527 | };
|
528 |
|
529 | |
530 |
|
531 |
|
532 |
|
533 |
|
534 |
|
535 |
|
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 |
|
543 |
|
544 |
|
545 |
|
546 |
|
547 |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 | Writer.prototype.render = function render (template, view, partials, config) {
|
553 | var tags = this.getConfigTags(config);
|
554 | var tokens = this.parse(template, tags);
|
555 | var context = (view instanceof Context) ? view : new Context(view, undefined);
|
556 | return this.renderTokens(tokens, context, partials, template, config);
|
557 | };
|
558 |
|
559 | |
560 |
|
561 |
|
562 |
|
563 |
|
564 |
|
565 |
|
566 |
|
567 |
|
568 | Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate, config) {
|
569 | var buffer = '';
|
570 |
|
571 | var token, symbol, value;
|
572 | for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
573 | value = undefined;
|
574 | token = tokens[i];
|
575 | symbol = token[0];
|
576 |
|
577 | if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate, config);
|
578 | else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate, config);
|
579 | else if (symbol === '>') value = this.renderPartial(token, context, partials, config);
|
580 | else if (symbol === '&') value = this.unescapedValue(token, context);
|
581 | else if (symbol === 'name') value = this.escapedValue(token, context, config);
|
582 | else if (symbol === 'text') value = this.rawValue(token);
|
583 |
|
584 | if (value !== undefined)
|
585 | buffer += value;
|
586 | }
|
587 |
|
588 | return buffer;
|
589 | };
|
590 |
|
591 | Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate, config) {
|
592 | var self = this;
|
593 | var buffer = '';
|
594 | var value = context.lookup(token[1]);
|
595 |
|
596 |
|
597 |
|
598 | function subRender (template) {
|
599 | return self.render(template, context, partials, config);
|
600 | }
|
601 |
|
602 | if (!value) return;
|
603 |
|
604 | if (isArray(value)) {
|
605 | for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
|
606 | buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate, config);
|
607 | }
|
608 | } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {
|
609 | buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate, config);
|
610 | } else if (isFunction(value)) {
|
611 | if (typeof originalTemplate !== 'string')
|
612 | throw new Error('Cannot use higher-order sections without the original template');
|
613 |
|
614 |
|
615 | value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);
|
616 |
|
617 | if (value != null)
|
618 | buffer += value;
|
619 | } else {
|
620 | buffer += this.renderTokens(token[4], context, partials, originalTemplate, config);
|
621 | }
|
622 | return buffer;
|
623 | };
|
624 |
|
625 | Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate, config) {
|
626 | var value = context.lookup(token[1]);
|
627 |
|
628 |
|
629 |
|
630 | if (!value || (isArray(value) && value.length === 0))
|
631 | return this.renderTokens(token[4], context, partials, originalTemplate, config);
|
632 | };
|
633 |
|
634 | Writer.prototype.indentPartial = function indentPartial (partial, indentation, lineHasNonSpace) {
|
635 | var filteredIndentation = indentation.replace(/[^ \t]/g, '');
|
636 | var partialByNl = partial.split('\n');
|
637 | for (var i = 0; i < partialByNl.length; i++) {
|
638 | if (partialByNl[i].length && (i > 0 || !lineHasNonSpace)) {
|
639 | partialByNl[i] = filteredIndentation + partialByNl[i];
|
640 | }
|
641 | }
|
642 | return partialByNl.join('\n');
|
643 | };
|
644 |
|
645 | Writer.prototype.renderPartial = function renderPartial (token, context, partials, config) {
|
646 | if (!partials) return;
|
647 | var tags = this.getConfigTags(config);
|
648 |
|
649 | var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
|
650 | if (value != null) {
|
651 | var lineHasNonSpace = token[6];
|
652 | var tagIndex = token[5];
|
653 | var indentation = token[4];
|
654 | var indentedValue = value;
|
655 | if (tagIndex == 0 && indentation) {
|
656 | indentedValue = this.indentPartial(value, indentation, lineHasNonSpace);
|
657 | }
|
658 | var tokens = this.parse(indentedValue, tags);
|
659 | return this.renderTokens(tokens, context, partials, indentedValue, config);
|
660 | }
|
661 | };
|
662 |
|
663 | Writer.prototype.unescapedValue = function unescapedValue (token, context) {
|
664 | var value = context.lookup(token[1]);
|
665 | if (value != null)
|
666 | return value;
|
667 | };
|
668 |
|
669 | Writer.prototype.escapedValue = function escapedValue (token, context, config) {
|
670 | var escape = this.getConfigEscape(config) || mustache.escape;
|
671 | var value = context.lookup(token[1]);
|
672 | if (value != null)
|
673 | return (typeof value === 'number' && escape === mustache.escape) ? String(value) : escape(value);
|
674 | };
|
675 |
|
676 | Writer.prototype.rawValue = function rawValue (token) {
|
677 | return token[1];
|
678 | };
|
679 |
|
680 | Writer.prototype.getConfigTags = function getConfigTags (config) {
|
681 | if (isArray(config)) {
|
682 | return config;
|
683 | }
|
684 | else if (config && typeof config === 'object') {
|
685 | return config.tags;
|
686 | }
|
687 | else {
|
688 | return undefined;
|
689 | }
|
690 | };
|
691 |
|
692 | Writer.prototype.getConfigEscape = function getConfigEscape (config) {
|
693 | if (config && typeof config === 'object' && !isArray(config)) {
|
694 | return config.escape;
|
695 | }
|
696 | else {
|
697 | return undefined;
|
698 | }
|
699 | };
|
700 |
|
701 | var mustache = {
|
702 | name: 'mustache.js',
|
703 | version: '4.2.0',
|
704 | tags: [ '{{', '}}' ],
|
705 | clearCache: undefined,
|
706 | escape: undefined,
|
707 | parse: undefined,
|
708 | render: undefined,
|
709 | Scanner: undefined,
|
710 | Context: undefined,
|
711 | Writer: undefined,
|
712 | |
713 |
|
714 |
|
715 |
|
716 |
|
717 | set templateCache (cache) {
|
718 | defaultWriter.templateCache = cache;
|
719 | },
|
720 | |
721 |
|
722 |
|
723 | get templateCache () {
|
724 | return defaultWriter.templateCache;
|
725 | }
|
726 | };
|
727 |
|
728 |
|
729 | var defaultWriter = new Writer();
|
730 |
|
731 | |
732 |
|
733 |
|
734 | mustache.clearCache = function clearCache () {
|
735 | return defaultWriter.clearCache();
|
736 | };
|
737 |
|
738 | |
739 |
|
740 |
|
741 |
|
742 |
|
743 | mustache.parse = function parse (template, tags) {
|
744 | return defaultWriter.parse(template, tags);
|
745 | };
|
746 |
|
747 | |
748 |
|
749 |
|
750 |
|
751 | mustache.render = function render (template, view, partials, config) {
|
752 | if (typeof template !== 'string') {
|
753 | throw new TypeError('Invalid template! Template should be a "string" ' +
|
754 | 'but "' + typeStr(template) + '" was given as the first ' +
|
755 | 'argument for mustache#render(template, view, partials)');
|
756 | }
|
757 |
|
758 | return defaultWriter.render(template, view, partials, config);
|
759 | };
|
760 |
|
761 |
|
762 |
|
763 | mustache.escape = escapeHtml;
|
764 |
|
765 |
|
766 | mustache.Scanner = Scanner;
|
767 | mustache.Context = Context;
|
768 | mustache.Writer = Writer;
|
769 |
|
770 | return mustache;
|
771 |
|
772 | })));
|