PageSourceSearch

https://www.matramax.ru/local/templates/matramax/components/bitrix…/modern-full/js/mustache.js

js matramax.ru collected 2026-09-28 11:11:30 UTC 20,306 bytes, 636 lines download raw bytes

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

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.