Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

763 строки
24KB

  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. //-- srp extension - to allow function parameters.
  343. // We assume name' is a function. otherwise parms are harmlessly ignored
  344. var parms = null;
  345. var parmIX = name.indexOf('(');
  346. if (parmIX > 0) {
  347. parms = name.substring(parmIX+1,name.length-1);
  348. name = name.substring(0,parmIX);
  349. //console.log("function with parms %s %s",name,parms);
  350. }
  351. //-- srp extension end
  352. if (cache.hasOwnProperty(name)) {
  353. value = cache[name];
  354. } else {
  355. var context = this, intermediateValue, names, index, lookupHit = false;
  356. while (context) {
  357. if (name.indexOf('.') > 0) {
  358. intermediateValue = context.view;
  359. names = name.split('.');
  360. index = 0;
  361. /**
  362. * Using the dot notion path in `name`, we descend through the
  363. * nested objects.
  364. *
  365. * To be certain that the lookup has been successful, we have to
  366. * check if the last object in the path actually has the property
  367. * we are looking for. We store the result in `lookupHit`.
  368. *
  369. * This is specially necessary for when the value has been set to
  370. * `undefined` and we want to avoid looking up parent contexts.
  371. *
  372. * In the case where dot notation is used, we consider the lookup
  373. * to be successful even if the last "object" in the path is
  374. * not actually an object but a primitive (e.g., a string, or an
  375. * integer), because it is sometimes useful to access a property
  376. * of an autoboxed primitive, such as the length of a string.
  377. **/
  378. while (intermediateValue != null && index < names.length) {
  379. if (index === names.length - 1)
  380. lookupHit = (
  381. hasProperty(intermediateValue, names[index])
  382. || primitiveHasOwnProperty(intermediateValue, names[index])
  383. );
  384. intermediateValue = intermediateValue[names[index++]];
  385. }
  386. } else {
  387. intermediateValue = context.view[name];
  388. /**
  389. * Only checking against `hasProperty`, which always returns `false` if
  390. * `context.view` is not an object. Deliberately omitting the check
  391. * against `primitiveHasOwnProperty` if dot notation is not used.
  392. *
  393. * Consider this example:
  394. * ```
  395. * Mustache.render("The length of a football field is {{#length}}{{length}}{{/length}}.", {length: "100 yards"})
  396. * ```
  397. *
  398. * If we were to check also against `primitiveHasOwnProperty`, as we do
  399. * in the dot notation case, then render call would return:
  400. *
  401. * "The length of a football field is 9."
  402. *
  403. * rather than the expected:
  404. *
  405. * "The length of a football field is 100 yards."
  406. **/
  407. lookupHit = hasProperty(context.view, name);
  408. }
  409. if (lookupHit) {
  410. value = intermediateValue;
  411. break;
  412. }
  413. context = context.parent;
  414. }
  415. cache[name] = value;
  416. }
  417. if (isFunction(value)) {
  418. // -- srp extension to allow parameters to be passed to the traget function
  419. //console.log("isFunction %s parms %s %o",name,parms,{context:this});
  420. if (parms) {
  421. if (parms.charAt(0) == '*') { // offload parsing to function
  422. value = value.apply(this.view,parms.substring(1));
  423. } else {
  424. var args = parms.split(/,/);
  425. value = value.apply(this.view,args);
  426. }
  427. } else {
  428. value = value.call(this.view);
  429. }
  430. // -- srp end extension
  431. }
  432. return value;
  433. };
  434. /**
  435. * A Writer knows how to take a stream of tokens and render them to a
  436. * string, given a context. It also maintains a cache of templates to
  437. * avoid the need to parse the same template twice.
  438. */
  439. function Writer () {
  440. this.templateCache = {
  441. _cache: {},
  442. set: function set (key, value) {
  443. this._cache[key] = value;
  444. },
  445. get: function get (key) {
  446. return this._cache[key];
  447. },
  448. clear: function clear () {
  449. this._cache = {};
  450. }
  451. };
  452. }
  453. /**
  454. * Clears all cached templates in this writer.
  455. */
  456. Writer.prototype.clearCache = function clearCache () {
  457. if (typeof this.templateCache !== 'undefined') {
  458. this.templateCache.clear();
  459. }
  460. };
  461. /**
  462. * Parses and caches the given `template` according to the given `tags` or
  463. * `mustache.tags` if `tags` is omitted, and returns the array of tokens
  464. * that is generated from the parse.
  465. */
  466. Writer.prototype.parse = function parse (template, tags) {
  467. var cache = this.templateCache;
  468. var cacheKey = template + ':' + (tags || mustache.tags).join(':');
  469. var isCacheEnabled = typeof cache !== 'undefined';
  470. var tokens = isCacheEnabled ? cache.get(cacheKey) : undefined;
  471. if (tokens == undefined) {
  472. tokens = parseTemplate(template, tags);
  473. isCacheEnabled && cache.set(cacheKey, tokens);
  474. }
  475. return tokens;
  476. };
  477. /**
  478. * High-level method that is used to render the given `template` with
  479. * the given `view`.
  480. *
  481. * The optional `partials` argument may be an object that contains the
  482. * names and templates of partials that are used in the template. It may
  483. * also be a function that is used to load partial templates on the fly
  484. * that takes a single argument: the name of the partial.
  485. *
  486. * If the optional `tags` argument is given here it must be an array with two
  487. * string values: the opening and closing tags used in the template (e.g.
  488. * [ "<%", "%>" ]). The default is to mustache.tags.
  489. */
  490. Writer.prototype.render = function render (template, view, partials, tags) {
  491. var tokens = this.parse(template, tags);
  492. var context = (view instanceof Context) ? view : new Context(view, undefined);
  493. return this.renderTokens(tokens, context, partials, template, tags);
  494. };
  495. /**
  496. * Low-level method that renders the given array of `tokens` using
  497. * the given `context` and `partials`.
  498. *
  499. * Note: The `originalTemplate` is only ever used to extract the portion
  500. * of the original template that was contained in a higher-order section.
  501. * If the template doesn't use higher-order sections, this argument may
  502. * be omitted.
  503. */
  504. Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate, tags) {
  505. var buffer = '';
  506. var token, symbol, value;
  507. for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
  508. value = undefined;
  509. token = tokens[i];
  510. symbol = token[0];
  511. if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate);
  512. else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate);
  513. else if (symbol === '>') value = this.renderPartial(token, context, partials, tags);
  514. else if (symbol === '&') value = this.unescapedValue(token, context);
  515. else if (symbol === 'name') value = this.escapedValue(token, context);
  516. else if (symbol === 'text') value = this.rawValue(token);
  517. if (value !== undefined)
  518. buffer += value;
  519. }
  520. return buffer;
  521. };
  522. Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) {
  523. var self = this;
  524. var buffer = '';
  525. var value = context.lookup(token[1]);
  526. // This function is used to render an arbitrary template
  527. // in the current context by higher-order sections.
  528. function subRender (template) {
  529. return self.render(template, context, partials);
  530. }
  531. if (!value) return;
  532. if (isArray(value)) {
  533. for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
  534. buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate);
  535. }
  536. } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {
  537. buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate);
  538. } else if (isFunction(value)) {
  539. if (typeof originalTemplate !== 'string')
  540. throw new Error('Cannot use higher-order sections without the original template');
  541. // Extract the portion of the original template that the section contains.
  542. value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);
  543. if (value != null)
  544. buffer += value;
  545. } else {
  546. buffer += this.renderTokens(token[4], context, partials, originalTemplate);
  547. }
  548. return buffer;
  549. };
  550. Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) {
  551. var value = context.lookup(token[1]);
  552. // Use JavaScript's definition of falsy. Include empty arrays.
  553. // See https://github.com/janl/mustache.js/issues/186
  554. if (!value || (isArray(value) && value.length === 0))
  555. return this.renderTokens(token[4], context, partials, originalTemplate);
  556. };
  557. Writer.prototype.indentPartial = function indentPartial (partial, indentation, lineHasNonSpace) {
  558. var filteredIndentation = indentation.replace(/[^ \t]/g, '');
  559. var partialByNl = partial.split('\n');
  560. for (var i = 0; i < partialByNl.length; i++) {
  561. if (partialByNl[i].length && (i > 0 || !lineHasNonSpace)) {
  562. partialByNl[i] = filteredIndentation + partialByNl[i];
  563. }
  564. }
  565. return partialByNl.join('\n');
  566. };
  567. Writer.prototype.renderPartial = function renderPartial (token, context, partials, tags) {
  568. if (!partials) return;
  569. var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
  570. if (value != null) {
  571. var lineHasNonSpace = token[6];
  572. var tagIndex = token[5];
  573. var indentation = token[4];
  574. var indentedValue = value;
  575. if (tagIndex == 0 && indentation) {
  576. indentedValue = this.indentPartial(value, indentation, lineHasNonSpace);
  577. }
  578. return this.renderTokens(this.parse(indentedValue, tags), context, partials, indentedValue, tags);
  579. }
  580. };
  581. Writer.prototype.unescapedValue = function unescapedValue (token, context) {
  582. var value = context.lookup(token[1]);
  583. if (value != null)
  584. return value;
  585. };
  586. Writer.prototype.escapedValue = function escapedValue (token, context) {
  587. var value = context.lookup(token[1]);
  588. if (value != null)
  589. return typeof value === 'number' ? String(value) : mustache.escape(value);
  590. };
  591. Writer.prototype.rawValue = function rawValue (token) {
  592. return token[1];
  593. };
  594. var mustache = {
  595. name: 'mustache.js',
  596. version: '4.0.1',
  597. tags: [ '{{', '}}' ],
  598. clearCache: undefined,
  599. escape: undefined,
  600. parse: undefined,
  601. render: undefined,
  602. Scanner: undefined,
  603. Context: undefined,
  604. Writer: undefined,
  605. /**
  606. * Allows a user to override the default caching strategy, by providing an
  607. * object with set, get and clear methods. This can also be used to disable
  608. * the cache by setting it to the literal `undefined`.
  609. */
  610. set templateCache (cache) {
  611. defaultWriter.templateCache = cache;
  612. },
  613. /**
  614. * Gets the default or overridden caching object from the default writer.
  615. */
  616. get templateCache () {
  617. return defaultWriter.templateCache;
  618. }
  619. };
  620. // All high-level mustache.* functions use this writer.
  621. var defaultWriter = new Writer();
  622. /**
  623. * Clears all cached templates in the default writer.
  624. */
  625. mustache.clearCache = function clearCache () {
  626. return defaultWriter.clearCache();
  627. };
  628. /**
  629. * Parses and caches the given template in the default writer and returns the
  630. * array of tokens it contains. Doing this ahead of time avoids the need to
  631. * parse templates on the fly as they are rendered.
  632. */
  633. mustache.parse = function parse (template, tags) {
  634. return defaultWriter.parse(template, tags);
  635. };
  636. /**
  637. * Renders the `template` with the given `view` and `partials` using the
  638. * default writer. If the optional `tags` argument is given here it must be an
  639. * array with two string values: the opening and closing tags used in the
  640. * template (e.g. [ "<%", "%>" ]). The default is to mustache.tags.
  641. */
  642. mustache.render = function render (template, view, partials, tags) {
  643. if (typeof template !== 'string') {
  644. throw new TypeError('Invalid template! Template should be a "string" ' +
  645. 'but "' + typeStr(template) + '" was given as the first ' +
  646. 'argument for mustache#render(template, view, partials)');
  647. }
  648. return defaultWriter.render(template, view, partials, tags);
  649. };
  650. // Export the escaping function so that the user may override it.
  651. // See https://github.com/janl/mustache.js/issues/244
  652. mustache.escape = escapeHtml;
  653. // Export these mainly for testing, but also for advanced usage.
  654. mustache.Scanner = Scanner;
  655. mustache.Context = Context;
  656. mustache.Writer = Writer;
  657. return mustache;
  658. })));