MDL-65758 library: Add Moodle modifications for Mustache
[moodle.git] / lib / amd / src / mustache.js
1 // The MIT License
2 //
3 // Copyright (c) 2009 Chris Wanstrath (Ruby)
4 // Copyright (c) 2010-2014 Jan Lehnardt (JavaScript)
5 // Copyright (c) 2010-2015 The mustache.js community
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 //
27 // Description of import into Moodle:
28 // Checkout from https://github.com/moodle/custom-mustache.js Branch: LAMBDA_ARGS
29 // Rebase onto latest release tag from https://github.com/janl/mustache.js
30 // Copy mustache.js into lib/amd/src/ in Moodle folder.
31 // Add the license as a comment to the file and these instructions.
32 // Add jshint tags so this file is not linted.
33 // Remove the "global define:" comment (hint for linter)
34 // Make sure that you have not removed the custom code for '$' and '<'.
36 /*!
37  * mustache.js - Logic-less {{mustache}} templates with JavaScript
38  * http://github.com/janl/mustache.js
39  */
41 /* jshint ignore:start */
43 (function defineMustache (global, factory) {
44   if (typeof exports === 'object' && exports && typeof exports.nodeName !== 'string') {
45     factory(exports); // CommonJS
46   } else if (typeof define === 'function' && define.amd) {
47     define(['exports'], factory); // AMD
48   } else {
49     global.Mustache = {};
50     factory(global.Mustache); // script, wsh, asp
51   }
52 }(this, function mustacheFactory (mustache) {
54   var objectToString = Object.prototype.toString;
55   var isArray = Array.isArray || function isArrayPolyfill (object) {
56     return objectToString.call(object) === '[object Array]';
57   };
59   function isFunction (object) {
60     return typeof object === 'function';
61   }
63   /**
64    * More correct typeof string handling array
65    * which normally returns typeof 'object'
66    */
67   function typeStr (obj) {
68     return isArray(obj) ? 'array' : typeof obj;
69   }
71   function escapeRegExp (string) {
72     return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
73   }
75   /**
76    * Null safe way of checking whether or not an object,
77    * including its prototype, has a given property
78    */
79   function hasProperty (obj, propName) {
80     return obj != null && typeof obj === 'object' && (propName in obj);
81   }
83   /**
84    * Safe way of detecting whether or not the given thing is a primitive and
85    * whether it has the given property
86    */
87   function primitiveHasOwnProperty (primitive, propName) {
88     return (
89       primitive != null
90       && typeof primitive !== 'object'
91       && primitive.hasOwnProperty
92       && primitive.hasOwnProperty(propName)
93     );
94   }
96   // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
97   // See https://github.com/janl/mustache.js/issues/189
98   var regExpTest = RegExp.prototype.test;
99   function testRegExp (re, string) {
100     return regExpTest.call(re, string);
101   }
103   var nonSpaceRe = /\S/;
104   function isWhitespace (string) {
105     return !testRegExp(nonSpaceRe, string);
106   }
108   var entityMap = {
109     '&': '&amp;',
110     '<': '&lt;',
111     '>': '&gt;',
112     '"': '&quot;',
113     "'": '&#39;',
114     '/': '&#x2F;',
115     '`': '&#x60;',
116     '=': '&#x3D;'
117   };
119   function escapeHtml (string) {
120     return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) {
121       return entityMap[s];
122     });
123   }
125   var whiteRe = /\s*/;
126   var spaceRe = /\s+/;
127   var equalsRe = /\s*=/;
128   var curlyRe = /\s*\}/;
129   var tagRe = /#|\^|\/|>|\{|&|=|!|\$|</;
131   /**
132    * Breaks up the given `template` string into a tree of tokens. If the `tags`
133    * argument is given here it must be an array with two string values: the
134    * opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of
135    * course, the default is to use mustaches (i.e. mustache.tags).
136    *
137    * A token is an array with at least 4 elements. The first element is the
138    * mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag
139    * did not contain a symbol (i.e. {{myValue}}) this element is "name". For
140    * all text that appears outside a symbol this element is "text".
141    *
142    * The second element of a token is its "value". For mustache tags this is
143    * whatever else was inside the tag besides the opening symbol. For text tokens
144    * this is the text itself.
145    *
146    * The third and fourth elements of the token are the start and end indices,
147    * respectively, of the token in the original template.
148    *
149    * Tokens that are the root node of a subtree contain two more elements: 1) an
150    * array of tokens in the subtree and 2) the index in the original template at
151    * which the closing tag for that section begins.
152    */
153   function parseTemplate (template, tags) {
154     if (!template)
155       return [];
157     var sections = [];     // Stack to hold section tokens
158     var tokens = [];       // Buffer to hold the tokens
159     var spaces = [];       // Indices of whitespace tokens on the current line
160     var hasTag = false;    // Is there a {{tag}} on the current line?
161     var nonSpace = false;  // Is there a non-space char on the current line?
163     // Strips all whitespace tokens array for the current line
164     // if there was a {{#tag}} on it and otherwise only space.
165     function stripSpace () {
166       if (hasTag && !nonSpace) {
167         while (spaces.length)
168           delete tokens[spaces.pop()];
169       } else {
170         spaces = [];
171       }
173       hasTag = false;
174       nonSpace = false;
175     }
177     var openingTagRe, closingTagRe, closingCurlyRe;
178     function compileTags (tagsToCompile) {
179       if (typeof tagsToCompile === 'string')
180         tagsToCompile = tagsToCompile.split(spaceRe, 2);
182       if (!isArray(tagsToCompile) || tagsToCompile.length !== 2)
183         throw new Error('Invalid tags: ' + tagsToCompile);
185       openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');
186       closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));
187       closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));
188     }
190     compileTags(tags || mustache.tags);
192     var scanner = new Scanner(template);
194     var start, type, value, chr, token, openSection;
195     while (!scanner.eos()) {
196       start = scanner.pos;
198       // Match any text between tags.
199       value = scanner.scanUntil(openingTagRe);
201       if (value) {
202         for (var i = 0, valueLength = value.length; i < valueLength; ++i) {
203           chr = value.charAt(i);
205           if (isWhitespace(chr)) {
206             spaces.push(tokens.length);
207           } else {
208             nonSpace = true;
209           }
211           tokens.push([ 'text', chr, start, start + 1 ]);
212           start += 1;
214           // Check for whitespace on the current line.
215           if (chr === '\n')
216             stripSpace();
217         }
218       }
220       // Match the opening tag.
221       if (!scanner.scan(openingTagRe))
222         break;
224       hasTag = true;
226       // Get the tag type.
227       type = scanner.scan(tagRe) || 'name';
228       scanner.scan(whiteRe);
230       // Get the tag value.
231       if (type === '=') {
232         value = scanner.scanUntil(equalsRe);
233         scanner.scan(equalsRe);
234         scanner.scanUntil(closingTagRe);
235       } else if (type === '{') {
236         value = scanner.scanUntil(closingCurlyRe);
237         scanner.scan(curlyRe);
238         scanner.scanUntil(closingTagRe);
239         type = '&';
240       } else {
241         value = scanner.scanUntil(closingTagRe);
242       }
244       // Match the closing tag.
245       if (!scanner.scan(closingTagRe))
246         throw new Error('Unclosed tag at ' + scanner.pos);
248       token = [ type, value, start, scanner.pos ];
249       tokens.push(token);
251       if (type === '#' || type === '^' || type === '$' || type === '<') {
252         sections.push(token);
253       } else if (type === '/') {
254         // Check section nesting.
255         openSection = sections.pop();
257         if (!openSection)
258           throw new Error('Unopened section "' + value + '" at ' + start);
260         if (openSection[1] !== value)
261           throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
262       } else if (type === 'name' || type === '{' || type === '&') {
263         nonSpace = true;
264       } else if (type === '=') {
265         // Set the tags for the next time around.
266         compileTags(value);
267       }
268     }
270     // Make sure there are no open sections when we're done.
271     openSection = sections.pop();
273     if (openSection)
274       throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
276     return nestTokens(squashTokens(tokens));
277   }
279   /**
280    * Combines the values of consecutive text tokens in the given `tokens` array
281    * to a single token.
282    */
283   function squashTokens (tokens) {
284     var squashedTokens = [];
286     var token, lastToken;
287     for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
288       token = tokens[i];
290       if (token) {
291         if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
292           lastToken[1] += token[1];
293           lastToken[3] = token[3];
294         } else {
295           squashedTokens.push(token);
296           lastToken = token;
297         }
298       }
299     }
301     return squashedTokens;
302   }
304   /**
305    * Forms the given array of `tokens` into a nested tree structure where
306    * tokens that represent a section have two additional items: 1) an array of
307    * all tokens that appear in that section and 2) the index in the original
308    * template that represents the end of that section.
309    */
310   function nestTokens (tokens) {
311     var nestedTokens = [];
312     var collector = nestedTokens;
313     var sections = [];
315     var token, section;
316     for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
317       token = tokens[i];
319       switch (token[0]) {
320         case '$':
321         case '<':
322         case '#':
323         case '^':
324           collector.push(token);
325           sections.push(token);
326           collector = token[4] = [];
327           break;
328         case '/':
329           section = sections.pop();
330           section[5] = token[2];
331           collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;
332           break;
333         default:
334           collector.push(token);
335       }
336     }
338     return nestedTokens;
339   }
341   /**
342    * A simple string scanner that is used by the template parser to find
343    * tokens in template strings.
344    */
345   function Scanner (string) {
346     this.string = string;
347     this.tail = string;
348     this.pos = 0;
349   }
351   /**
352    * Returns `true` if the tail is empty (end of string).
353    */
354   Scanner.prototype.eos = function eos () {
355     return this.tail === '';
356   };
358   /**
359    * Tries to match the given regular expression at the current position.
360    * Returns the matched text if it can match, the empty string otherwise.
361    */
362   Scanner.prototype.scan = function scan (re) {
363     var match = this.tail.match(re);
365     if (!match || match.index !== 0)
366       return '';
368     var string = match[0];
370     this.tail = this.tail.substring(string.length);
371     this.pos += string.length;
373     return string;
374   };
376   /**
377    * Skips all text until the given regular expression can be matched. Returns
378    * the skipped string, which is the entire tail if no match can be made.
379    */
380   Scanner.prototype.scanUntil = function scanUntil (re) {
381     var index = this.tail.search(re), match;
383     switch (index) {
384       case -1:
385         match = this.tail;
386         this.tail = '';
387         break;
388       case 0:
389         match = '';
390         break;
391       default:
392         match = this.tail.substring(0, index);
393         this.tail = this.tail.substring(index);
394     }
396     this.pos += match.length;
398     return match;
399   };
401   /**
402    * Represents a rendering context by wrapping a view object and
403    * maintaining a reference to the parent context.
404    */
405   function Context (view, parentContext) {
406     this.view = view;
407     this.blocks = {};
408     this.cache = { '.': this.view };
409     this.parent = parentContext;
410   }
412   /**
413    * Creates a new context using the given view with this context
414    * as the parent.
415    */
416   Context.prototype.push = function push (view) {
417     return new Context(view, this);
418   };
420   /**
421    * Set a value in the current block context.
422    */
423   Context.prototype.setBlockVar = function set (name, value) {
424     var blocks = this.blocks;
426     blocks[name] = value;
428     return value;
429   };
431   /**
432    * Clear all current block vars.
433    */
434   Context.prototype.clearBlockVars = function clearBlockVars () {
435     this.blocks = {};
436   };
438   /**
439    * Get a value only from the current block context.
440    */
441   Context.prototype.getBlockVar = function getBlockVar (name) {
442     var blocks = this.blocks;
444     var value;
445     if (blocks.hasOwnProperty(name)) {
446       value = blocks[name];
447     } else {
448       if (this.parent) {
449         value = this.parent.getBlockVar(name);
450       }
451     }
452     // Can return undefined.
453     return value;
454   };
456   /**
457    * Returns the value of the given name in this context, traversing
458    * up the context hierarchy if the value is absent in this context's view.
459    */
460   Context.prototype.lookup = function lookup (name) {
461     var cache = this.cache;
463     var value;
464     if (cache.hasOwnProperty(name)) {
465       value = cache[name];
466     } else {
467       var context = this, intermediateValue, names, index, lookupHit = false;
469       while (context) {
470         if (name.indexOf('.') > 0) {
471           intermediateValue = context.view;
472           names = name.split('.');
473           index = 0;
475           /**
476            * Using the dot notion path in `name`, we descend through the
477            * nested objects.
478            *
479            * To be certain that the lookup has been successful, we have to
480            * check if the last object in the path actually has the property
481            * we are looking for. We store the result in `lookupHit`.
482            *
483            * This is specially necessary for when the value has been set to
484            * `undefined` and we want to avoid looking up parent contexts.
485            *
486            * In the case where dot notation is used, we consider the lookup
487            * to be successful even if the last "object" in the path is
488            * not actually an object but a primitive (e.g., a string, or an
489            * integer), because it is sometimes useful to access a property
490            * of an autoboxed primitive, such as the length of a string.
491            **/
492           while (intermediateValue != null && index < names.length) {
493             if (index === names.length - 1)
494               lookupHit = (
495                 hasProperty(intermediateValue, names[index])
496                 || primitiveHasOwnProperty(intermediateValue, names[index])
497               );
499             intermediateValue = intermediateValue[names[index++]];
500           }
501         } else {
502           intermediateValue = context.view[name];
504           /**
505            * Only checking against `hasProperty`, which always returns `false` if
506            * `context.view` is not an object. Deliberately omitting the check
507            * against `primitiveHasOwnProperty` if dot notation is not used.
508            *
509            * Consider this example:
510            * ```
511            * Mustache.render("The length of a football field is {{#length}}{{length}}{{/length}}.", {length: "100 yards"})
512            * ```
513            *
514            * If we were to check also against `primitiveHasOwnProperty`, as we do
515            * in the dot notation case, then render call would return:
516            *
517            * "The length of a football field is 9."
518            *
519            * rather than the expected:
520            *
521            * "The length of a football field is 100 yards."
522            **/
523           lookupHit = hasProperty(context.view, name);
524         }
526         if (lookupHit) {
527           value = intermediateValue;
528           break;
529         }
531         context = context.parent;
532       }
534       cache[name] = value;
535     }
537     if (isFunction(value))
538       value = value.call(this.view);
540     return value;
541   };
543   /**
544    * A Writer knows how to take a stream of tokens and render them to a
545    * string, given a context. It also maintains a cache of templates to
546    * avoid the need to parse the same template twice.
547    */
548   function Writer () {
549     this.cache = {};
550   }
552   /**
553    * Clears all cached templates in this writer.
554    */
555   Writer.prototype.clearCache = function clearCache () {
556     this.cache = {};
557   };
559   /**
560    * Parses and caches the given `template` according to the given `tags` or
561    * `mustache.tags` if `tags` is omitted,  and returns the array of tokens
562    * that is generated from the parse.
563    */
564   Writer.prototype.parse = function parse (template, tags) {
565     var cache = this.cache;
566     var cacheKey = template + ':' + (tags || mustache.tags).join(':');
567     var tokens = cache[cacheKey];
569     if (tokens == null)
570       tokens = cache[cacheKey] = parseTemplate(template, tags);
572     return tokens;
573   };
575   /**
576    * High-level method that is used to render the given `template` with
577    * the given `view`.
578    *
579    * The optional `partials` argument may be an object that contains the
580    * names and templates of partials that are used in the template. It may
581    * also be a function that is used to load partial templates on the fly
582    * that takes a single argument: the name of the partial.
583    *
584    * If the optional `tags` argument is given here it must be an array with two
585    * string values: the opening and closing tags used in the template (e.g.
586    * [ "<%", "%>" ]). The default is to mustache.tags.
587    */
588   Writer.prototype.render = function render (template, view, partials, tags) {
589     var tokens = this.parse(template, tags);
590     var context = (view instanceof Context) ? view : new Context(view);
591     return this.renderTokens(tokens, context, partials, template, tags);
592   };
594   /**
595    * Low-level method that renders the given array of `tokens` using
596    * the given `context` and `partials`.
597    *
598    * Note: The `originalTemplate` is only ever used to extract the portion
599    * of the original template that was contained in a higher-order section.
600    * If the template doesn't use higher-order sections, this argument may
601    * be omitted.
602    */
603   Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate, tags) {
604     var buffer = '';
606     var token, symbol, value;
607     for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
608       value = undefined;
609       token = tokens[i];
610       symbol = token[0];
612       if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate);
613       else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate);
614       else if (symbol === '>') value = this.renderPartial(token, context, partials, tags);
615       else if (symbol === '<') value = this.renderBlock(token, context, partials, originalTemplate);
616       else if (symbol === '$') value = this.renderBlockVariable(token, context, partials, originalTemplate);
617       else if (symbol === '&') value = this.unescapedValue(token, context);
618       else if (symbol === 'name') value = this.escapedValue(token, context);
619       else if (symbol === 'text') value = this.rawValue(token);
621       if (value !== undefined)
622         buffer += value;
623     }
625     return buffer;
626   };
628   Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) {
629     var self = this;
630     var buffer = '';
631     var value = context.lookup(token[1]);
633     // This function is used to render an arbitrary template
634     // in the current context by higher-order sections.
635     function subRender (template) {
636       return self.render(template, context, partials);
637     }
639     if (!value) return;
641     if (isArray(value)) {
642       for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
643         buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate);
644       }
645     } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {
646       buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate);
647     } else if (isFunction(value)) {
648       if (typeof originalTemplate !== 'string')
649         throw new Error('Cannot use higher-order sections without the original template');
651       // Extract the portion of the original template that the section contains.
652       value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);
654       if (value != null)
655         buffer += value;
656     } else {
657       buffer += this.renderTokens(token[4], context, partials, originalTemplate);
658     }
659     return buffer;
660   };
662   Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) {
663     var value = context.lookup(token[1]);
665     // Use JavaScript's definition of falsy. Include empty arrays.
666     // See https://github.com/janl/mustache.js/issues/186
667     if (!value || (isArray(value) && value.length === 0))
668       return this.renderTokens(token[4], context, partials, originalTemplate);
669   };
671   Writer.prototype.renderPartial = function renderPartial (token, context, partials, tags) {
672     if (!partials) return;
674     var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
675     if (value != null)
676       return this.renderTokens(this.parse(value, tags), context, partials, value);
677   };
679   Writer.prototype.renderBlock = function renderBlock (token, context, partials, originalTemplate) {
680     if (!partials) return;
682     var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
683     if (value != null)
684       // Ignore any wrongly set block vars before we started.
685       context.clearBlockVars();
686       // We are only rendering to record the default block variables.
687       this.renderTokens(token[4], context, partials, originalTemplate);
688       // Now we render and return the result.
689       var result = this.renderTokens(this.parse(value), context, partials, value);
690       // Don't leak the block variables outside this include.
691       context.clearBlockVars();
692       return result;
693   };
695   Writer.prototype.renderBlockVariable = function renderBlockVariable (token, context, partials, originalTemplate) {
696     var value = token[1];
698     var exists = context.getBlockVar(value);
699     if (!exists) {
700       context.setBlockVar(value, originalTemplate.slice(token[3], token[5]));
701       return this.renderTokens(token[4], context, partials, originalTemplate);
702     } else {
703       return this.renderTokens(this.parse(exists), context, partials, exists);
704     }
705   };
707   Writer.prototype.unescapedValue = function unescapedValue (token, context) {
708     var value = context.lookup(token[1]);
709     if (value != null)
710       return value;
711   };
713   Writer.prototype.escapedValue = function escapedValue (token, context) {
714     var value = context.lookup(token[1]);
715     if (value != null)
716       return mustache.escape(value);
717   };
719   Writer.prototype.rawValue = function rawValue (token) {
720     return token[1];
721   };
723   mustache.name = 'mustache.js';
724   mustache.version = '3.0.1';
725   mustache.tags = [ '{{', '}}' ];
727   // All high-level mustache.* functions use this writer.
728   var defaultWriter = new Writer();
730   /**
731    * Clears all cached templates in the default writer.
732    */
733   mustache.clearCache = function clearCache () {
734     return defaultWriter.clearCache();
735   };
737   /**
738    * Parses and caches the given template in the default writer and returns the
739    * array of tokens it contains. Doing this ahead of time avoids the need to
740    * parse templates on the fly as they are rendered.
741    */
742   mustache.parse = function parse (template, tags) {
743     return defaultWriter.parse(template, tags);
744   };
746   /**
747    * Renders the `template` with the given `view` and `partials` using the
748    * default writer. If the optional `tags` argument is given here it must be an
749    * array with two string values: the opening and closing tags used in the
750    * template (e.g. [ "<%", "%>" ]). The default is to mustache.tags.
751    */
752   mustache.render = function render (template, view, partials, tags) {
753     if (typeof template !== 'string') {
754       throw new TypeError('Invalid template! Template should be a "string" ' +
755                           'but "' + typeStr(template) + '" was given as the first ' +
756                           'argument for mustache#render(template, view, partials)');
757     }
759     return defaultWriter.render(template, view, partials, tags);
760   };
762   // This is here for backwards compatibility with 0.4.x.,
763   /*eslint-disable */ // eslint wants camel cased function name
764   mustache.to_html = function to_html (template, view, partials, send) {
765     /*eslint-enable*/
767     var result = mustache.render(template, view, partials);
769     if (isFunction(send)) {
770       send(result);
771     } else {
772       return result;
773     }
774   };
776   // Export the escaping function so that the user may override it.
777   // See https://github.com/janl/mustache.js/issues/244
778   mustache.escape = escapeHtml;
780   // Export these mainly for testing, but also for advanced usage.
781   mustache.Scanner = Scanner;
782   mustache.Context = Context;
783   mustache.Writer = Writer;
785   return mustache;
786 }));
787 /* jshint ignore:end */