PageSourceSearch

https://www.sawai.co.jp/assets/js/vendor/mustache.js

js sawai.co.jp collected 2026-10-02 06:20:58 UTC 14,854 bytes, 536 lines download raw bytes

1/*!
2 * mustache.js - Logic-less {{mustache}} templates with JavaScript
3 * http://github.com/janl/mustache.js
4 */
5
6/*global define: false*/
7
8(function (root, factory) {
9  if (typeof exports === "object" && exports) {
10    factory(exports); // CommonJS
11  } else {
12    var mustache = {};
13    factory(mustache);
14    if (typeof define === "function" && define.amd) {
15      define(mustache); // AMD
16    } else {
17      root.Mustache = mustache; // <script>
18    }
19  }
20}(this, function (mustache) {
21
22  var whiteRe = /\s*/;
23  var spaceRe = /\s+/;
24  var nonSpaceRe = /\S/;
25  var eqRe = /\s*=/;
26  var curlyRe = /\s*\}/;
27  var tagRe = /#|\^|\/|>|\{|&|=|!/;
28
29  // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
30  // See https://github.com/janl/mustache.js/issues/189
31  var RegExp_test = RegExp.prototype.test;
32  function testRegExp(re, string) {
33    return RegExp_test.call(re, string);
34  }
35
36  function isWhitespace(string) {
37    return !testRegExp(nonSpaceRe, string);
38  }
39
40  var Object_toString = Object.prototype.toString;
41  var isArray = Array.isArray || function (obj) {
42    return Object_toString.call(obj) === '[object Array]';
43  };
44
45  function escapeRegExp(string) {
46    return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
47  }
48
49  var entityMap = {
50    "&": "&amp;",
51    "<": "&lt;",
52    ">": "&gt;",
53    '"': '&quot;',
54    "'": '&#39;',
55    "/": '&#x2F;'
56  };
57
58  function escapeHtml(string) {
59    return String(string).replace(/[&<>"'\/]/g, function (s) {
60      return entityMap[s];
61    });
62  }
63
64  function Scanner(string) {
65    this.string = string;
66    this.tail = string;
67    this.pos = 0;
68  }
69
70  /**
71   * Returns `true` if the tail is empty (end of string).
72   */
73  Scanner.prototype.eos = function () {
74    return this.tail === "";
75  };
76
77  /**
78   * Tries to match the given regular expression at the current position.
79   * Returns the matched text if it can match, the empty string otherwise.
80   */
81  Scanner.prototype.scan = function (re) {
82    var match = this.tail.match(re);
83
84    if (match && match.index === 0) {
85      this.tail = this.tail.substring(match[0].length);
86      this.pos += match[0].length;
87      return match[0];
88    }
89
90    return "";
91  };
92
93  /**
94   * Skips all text until the given regular expression can be matched. Returns
95   * the skipped string, which is the entire tail if no match can be made.
96   */
97  Scanner.prototype.scanUntil = function (re) {
98    var match, pos = this.tail.search(re);
99
100    switch (pos) {
101    case -1:
102      match = this.tail;
103      this.pos += this.tail.length;
104      this.tail = "";
105      break;
106    case 0:
107      match = "";
108      break;
109    default:
110      match = this.tail.substring(0, pos);
111      this.tail = this.tail.substring(pos);
112      this.pos += pos;
113    }
114
115    return match;
116  };
117
118  function Context(view, parent) {
119    this.view = view || {};
120    this.parent = parent;
121    this._cache = {};
122  }
123
124  Context.make = function (view) {
125    return (view instanceof Context) ? view : new Context(view);
126  };
127
128  Context.prototype.push = function (view) {
129    return new Context(view, this);
130  };
131
132  Context.prototype.lookup = function (name) {
133    var value = this._cache[name];
134
135    if (!value) {
136      if (name == '.') {
137        value = this.view;
138      } else {
139        var context = this;
140
141        while (context) {
142          if (name.indexOf('.') > 0) {
143            value = context.view;
144            var names = name.split('.'), i = 0;
145            while (value && i < names.length) {
146              value = value[names[i++]];
147            }
148          } else {
149            value = context.view[name];
150          }
151
152          if (value != null) break;
153
154          context = context.parent;
155        }
156      }
157
158      this._cache[name] = value;
159    }
160
161    if (typeof value === 'function') value = value.call(this.view);
162
163    return value;
164  };
165
166  function Writer() {
167    this.clearCache();
168  }
169
170  Writer.prototype.clearCache = function () {
171    this._cache = {};
172    this._partialCache = {};
173  };
174
175  Writer.prototype.compile = function (template, tags) {
176    var fn = this._cache[template];
177
178    if (!fn) {
179      var tokens = mustache.parse(template, tags);
180      fn = this._cache[template] = this.compileTokens(tokens, template);
181    }
182
183    return fn;
184  };
185
186  Writer.prototype.compilePartial = function (name, template, tags) {
187    var fn = this.compile(template, tags);
188    this._partialCache[name] = fn;
189    return fn;
190  };
191
192  Writer.prototype.getPartial = function (name) {
193    if (!(name in this._partialCache) && this._loadPartial) {
194      this.compilePartial(name, this._loadPartial(name));
195    }
196
197    return this._partialCache[name];
198  };
199
200  Writer.prototype.compileTokens = function (tokens, template) {
201    var self = this;
202    return function (view, partials) {
203      if (partials) {
204        if (typeof partials === 'function') {
205          self._loadPartial = partials;
206        } else {
207          for (var name in partials) {
208            self.compilePartial(name, partials[name]);
209          }
210        }
211      }
212
213      return renderTokens(tokens, self, Context.make(view), template);
214    };
215  };
216
217  Writer.prototype.render = function (template, view, partials) {
218    return this.compile(template)(view, partials);
219  };
220
221  /**
222   * Low-level function that renders the given `tokens` using the given `writer`
223   * and `context`. The `template` string is only needed for templates that use
224   * higher-order sections to extract the portion of the original template that
225   * was contained in that section.
226   */
227  function renderTokens(tokens, writer, context, template) {
228    var buffer = '';
229
230    var token, tokenValue, value;
231    for (var i = 0, len = tokens.length; i < len; ++i) {
232      token = tokens[i];
233      tokenValue = token[1];
234
235      switch (token[0]) {
236      case '#':
237        value = context.lookup(tokenValue);
238
239        if (typeof value === 'object') {
240          if (isArray(value)) {
241            for (var j = 0, jlen = value.length; j < jlen; ++j) {
242              buffer += renderTokens(token[4], writer, context.push(value[j]), template);
243            }
244          } else if (value) {
245            buffer += renderTokens(token[4], writer, context.push(value), template);
246          }
247        } else if (typeof value === 'function') {
248          var text = template == null ? null : template.slice(token[3], token[5]);
249          value = value.call(context.view, text, function (template) {
250            return writer.render(template, context);
251          });
252          if (value != null) buffer += value;
253        } else if (value) {
254          buffer += renderTokens(token[4], writer, context, template);
255        }
256
257        break;
258      case '^':
259        value = context.lookup(tokenValue);
260
261        // Use JavaScript's definition of falsy. Include empty arrays.
262        // See https://github.com/janl/mustache.js/issues/186
263        if (!value || (isArray(value) && value.length === 0)) {
264          buffer += renderTokens(token[4], writer, context, template);
265        }
266
267        break;
268      case '>':
269        value = writer.getPartial(tokenValue);
270        if (typeof value === 'function') buffer += value(context);
271        break;
272      case '&':
273        value = context.lookup(tokenValue);
274        if (value != null) buffer += value;
275        break;
276      case 'name':
277        value = context.lookup(tokenValue);
278        if (value != null) buffer += mustache.escape(value);
279        break;
280      case 'text':
281        buffer += tokenValue;
282        break;
283      }
284    }
285
286    return buffer;
287  }
288
289  /**
290   * Forms the given array of `tokens` into a nested tree structure where
291   * tokens that represent a section have two additional items: 1) an array of
292   * all tokens that appear in that section and 2) the index in the original
293   * template that represents the end of that section.
294   */
295  function nestTokens(tokens) {
296    var tree = [];
297    var collector = tree;
298    var sections = [];
299
300    var token;
301    for (var i = 0, len = tokens.length; i < len; ++i) {
302      token = tokens[i];
303      switch (token[0]) {
304      case '#':
305      case '^':
306        sections.push(token);
307        collector.push(token);
308        collector = token[4] = [];
309        break;
310      case '/':
311        var section = sections.pop();
312        section[5] = token[2];
313        collector = sections.length > 0 ? sections[sections.length - 1][4] : tree;
314        break;
315      default:
316        collector.push(token);
317      }
318    }
319
320    return tree;
321  }
322
323  /**
324   * Combines the values of consecutive text tokens in the given `tokens` array
325   * to a single token.
326   */
327  function squashTokens(tokens) {
328    var squashedTokens = [];
329
330    var token, lastToken;
331    for (var i = 0, len = tokens.length; i < len; ++i) {
332      token = tokens[i];
333      if (token) {
334        if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
335          lastToken[1] += token[1];
336          lastToken[3] = token[3];
337        } else {
338          lastToken = token;
339          squashedTokens.push(token);
340        }
341      }
342    }
343
344    return squashedTokens;
345  }
346
347  function escapeTags(tags) {
348    return [
349      new RegExp(escapeRegExp(tags[0]) + "\\s*"),
350      new RegExp("\\s*" + escapeRegExp(tags[1]))
351    ];
352  }
353
354  /**
355   * Breaks up the given `template` string into a tree of token objects. If
356   * `tags` is given here it must be an array with two string values: the
357   * opening and closing tags used in the template (e.g. ["<%", "%>"]). Of
358   * course, the default is to use mustaches (i.e. Mustache.tags).
359   */
360  function parseTemplate(template, tags) {
361    template = template || '';
362    tags = tags || mustache.tags;
363
364    if (typeof tags === 'string') tags = tags.split(spaceRe);
365    if (tags.length !== 2) throw new Error('Invalid tags: ' + tags.join(', '));
366
367    var tagRes = escapeTags(tags);
368    var scanner = new Scanner(template);
369
370    var sections = [];     // Stack to hold section tokens
371    var tokens = [];       // Buffer to hold the tokens
372    var spaces = [];       // Indices of whitespace tokens on the current line
373    var hasTag = false;    // Is there a {{tag}} on the current line?
374    var nonSpace = false;  // Is there a non-space char on the current line?
375
376    // Strips all whitespace tokens array for the current line
377    // if there was a {{#tag}} on it and otherwise only space.
378    function stripSpace() {
379      if (hasTag && !nonSpace) {
380        while (spaces.length) {
381          delete tokens[spaces.pop()];
382        }
383      } else {
384        spaces = [];
385      }
386
387      hasTag = false;
388      nonSpace = false;
389    }
390
391    var start, type, value, chr, token;
392    while (!scanner.eos()) {
393      start = scanner.pos;
394
395      // Match any text between tags.
396      value = scanner.scanUntil(tagRes[0]);
397      if (value) {
398        for (var i = 0, len = value.length; i < len; ++i) {
399          chr = value.charAt(i);
400
401          if (isWhitespace(chr)) {
402            spaces.push(tokens.length);
403          } else {
404            nonSpace = true;
405          }
406
407          tokens.push(['text', chr, start, start + 1]);
408          start += 1;
409
410          // Check for whitespace on the current line.
411          if (chr == '\n') stripSpace();
412        }
413      }
414
415      // Match the opening tag.
416      if (!scanner.scan(tagRes[0])) break;
417      hasTag = true;
418
419      // Get the tag type.
420      type = scanner.scan(tagRe) || 'name';
421      scanner.scan(whiteRe);
422
423      // Get the tag value.
424      if (type === '=') {
425        value = scanner.scanUntil(eqRe);
426        scanner.scan(eqRe);
427        scanner.scanUntil(tagRes[1]);
428      } else if (type === '{') {
429        value = scanner.scanUntil(new RegExp('\\s*' + escapeRegExp('}' + tags[1])));
430        scanner.scan(curlyRe);
431        scanner.scanUntil(tagRes[1]);
432        type = '&';
433      } else {
434        value = scanner.scanUntil(tagRes[1]);
435      }
436
437      // Match the closing tag.
438      if (!scanner.scan(tagRes[1])) throw new Error('Unclosed tag at ' + scanner.pos);
439
440      token = [type, value, start, scanner.pos];
441      tokens.push(token);
442
443      if (type === '#' || type === '^') {
444        sections.push(token);
445      } else if (type === '/') {
446        // Check section nesting.
447        if (sections.length === 0) throw new Error('Unopened section "' + value + '" at ' + start);
448        var openSection = sections.pop();
449        if (openSection[1] !== value) throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
450      } else if (type === 'name' || type === '{' || type === '&') {
451        nonSpace = true;
452      } else if (type === '=') {
453        // Set the tags for the next time around.
454        tags = value.split(spaceRe);
455        if (tags.length !== 2) throw new Error('Invalid tags at ' + start + ': ' + tags.join(', '));
456        tagRes = escapeTags(tags);
457      }
458    }
459
460    // Make sure there are no open sections when we're done.
461    var openSection = sections.pop();
462    if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
463
464    tokens = squashTokens(tokens);
465
466    return nestTokens(tokens);
467  }
468
469  mustache.name = "mustache.js";
470  mustache.version = "0.7.2";
471  mustache.tags = ["{{", "}}"];
472
473  mustache.Scanner = Scanner;
474  mustache.Context = Context;
475  mustache.Writer = Writer;
476
477  mustache.parse = parseTemplate;
478
479  // Export the escaping function so that the user may override it.
480  // See https://github.com/janl/mustache.js/issues/244
481  mustache.escape = escapeHtml;
482
483  // All Mustache.* functions use this writer.
484  var defaultWriter = new Writer();
485
486  /**
487   * Clears all cached templates and partials in the default writer.
488   */
489  mustache.clearCache = function () {
490    return defaultWriter.clearCache();
491  };
492
493  /**
494   * Compiles the given `template` to a reusable function using the default
495   * writer.
496   */
497  mustache.compile = function (template, tags) {
498    return defaultWriter.compile(template, tags);
499  };
500
501  /**
502   * Compiles the partial with the given `name` and `template` to a reusable
503   * function using the default writer.
504   */
505  mustache.compilePartial = function (name, template, tags) {
506    return defaultWriter.compilePartial(name, template, tags);
507  };
508
509  /**
510   * Compiles the given array of tokens (the output of a parse) to a reusable
511   * function using the default writer.
512   */
513  mustache.compileTokens = function (tokens, template) {
514    return defaultWriter.compileTokens(tokens, template);
515  };
516
517  /**
518   * Renders the `template` with the given `view` and `partials` using the
519   * default writer.
520   */
521  mustache.render = function (template, view, partials) {
522    return defaultWriter.render(template, view, partials);
523  };
524
525  // This is here for backwards compatibility with 0.4.x.
526  mustache.to_html = function (template, view, partials, send) {
527    var result = mustache.render(template, view, partials);
528
529    if (typeof send === "function") {
530      send(result);
531    } else {
532      return result;
533    }
534  };
535
536}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.