mustache.js 23 KB

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