PageSourceSearch

https://branches.ridgewoodbank.com/permanent-b0b701/js/875/7bd2e03…1ee4e960d9b319cec6707351621ad402d245/all.js

js ridgewoodbank.com collected 2026-09-24 21:10:43 UTC 94,084 bytes, 2,860 lines download raw bytes

1// Production steps of ECMA-262, Edition 6, 22.1.2.1
2if (!Array.from) {
3  Array.from = (function () {
4    var toStr = Object.prototype.toString;
5    var isCallable = function (fn) {
6      return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
7    };
8    var toInteger = function (value) {
9      var number = Number(value);
10      if (isNaN(number)) { return 0; }
11      if (number === 0 || !isFinite(number)) { return number; }
12      return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
13    };
14    var maxSafeInteger = Math.pow(2, 53) - 1;
15    var toLength = function (value) {
16      var len = toInteger(value);
17      return Math.min(Math.max(len, 0), maxSafeInteger);
18    };
19
20    // The length property of the from method is 1.
21    return function from(arrayLike/*, mapFn, thisArg */) {
22      // 1. Let C be the this value.
23      var C = this;
24
25      // 2. Let items be ToObject(arrayLike).
26      var items = Object(arrayLike);
27
28      // 3. ReturnIfAbrupt(items).
29      if (arrayLike == null) {
30        throw new TypeError('Array.from requires an array-like object - not null or undefined');
31      }
32
33      // 4. If mapfn is undefined, then let mapping be false.
34      var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
35      var T;
36      if (typeof mapFn !== 'undefined') {
37        // 5. else
38        // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
39        if (!isCallable(mapFn)) {
40          throw new TypeError('Array.from: when provided, the second argument must be a function');
41        }
42
43        // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
44        if (arguments.length > 2) {
45          T = arguments[2];
46        }
47      }
48
49      // 10. Let lenValue be Get(items, "length").
50      // 11. Let len be ToLength(lenValue).
51      var len = toLength(items.length);
52
53      // 13. If IsConstructor(C) is true, then
54      // 13. a. Let A be the result of calling the [[Construct]] internal method
55      // of C with an argument list containing the single item len.
56      // 14. a. Else, Let A be ArrayCreate(len).
57      var A = isCallable(C) ? Object(new C(len)) : new Array(len);
58
59      // 16. Let k be 0.
60      var k = 0;
61      // 17. Repeat, while k < len… (also steps a - h)
62      var kValue;
63      while (k < len) {
64        kValue = items[k];
65        if (mapFn) {
66          A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
67        } else {
68          A[k] = kValue;
69        }
70        k += 1;
71      }
72      // 18. Let putStatus be Put(A, "length", len, true).
73      A.length = len;
74      // 20. Return A.
75      return A;
76    };
77  }());
78}
79
80/*
81 * Copyright 2008 Google Inc.
82 *
83 * Licensed under the Apache License, Version 2.0 (the "License");
84 * you may not use this file except in compliance with the License.
85 * You may obtain a copy of the License at
86 *
87 *     http://www.apache.org/licenses/LICENSE-2.0
88 *
89 * Unless required by applicable law or agreed to in writing, software
90 * distributed under the License is distributed on an "AS IS" BASIS,
91 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92 * See the License for the specific language governing permissions and
93 * limitations under the License.
94 */
95
96/**
97 * @fileoverview
98 * Utility functions and classes for Soy.
99 *
100 * <p>
101 * The top portion of this file contains utilities for Soy users:<ul>
102 *   <li> soy.StringBuilder: Compatible with the 'stringbuilder' code style.
103 *   <li> soy.renderElement: Render template and set as innerHTML of an element.
104 *   <li> soy.renderAsFragment: Render template and return as HTML fragment.
105 * </ul>
106 *
107 * <p>
108 * The bottom portion of this file contains utilities that should only be called
109 * by Soy-generated JS code. Please do not use these functions directly from
110 * your hand-writen code. Their names all start with '$$'.
111 *
112 * @author Garrett Boyer
113 * @author Mike Samuel
114 * @author Kai Huang
115 * @author Aharon Lanin
116 */
117
118
119// COPIED FROM nogoog_shim.js
120
121// Create closure namespaces.
122var goog = goog || {};
123
124
125goog.DEBUG = false;
126
127
128goog.inherits = function(childCtor, parentCtor) {
129  /** @constructor */
130  function tempCtor() {}
131  tempCtor.prototype = parentCtor.prototype;
132  childCtor.superClass_ = parentCtor.prototype;
133  childCtor.prototype = new tempCtor();
134  childCtor.prototype.constructor = childCtor;
135};
136
137
138// Just enough browser detection for this file.
139if (!goog.userAgent) {
140  goog.userAgent = (function() {
141    var userAgent = "";
142    if ("undefined" !== typeof navigator && navigator
143        && "string" == typeof navigator.userAgent) {
144      userAgent = navigator.userAgent;
145    }
146    var isOpera = userAgent.indexOf('Opera') == 0;
147    return {
148      jscript: {
149        /**
150         * @type {boolean}
151         */
152        HAS_JSCRIPT: 'ScriptEngine' in this
153      },
154      /**
155       * @type {boolean}
156       */
157      OPERA: isOpera,
158      /**
159       * @type {boolean}
160       */
161      IE: !isOpera && userAgent.indexOf('MSIE') != -1,
162      /**
163       * @type {boolean}
164       */
165      WEBKIT: !isOpera && userAgent.indexOf('WebKit') != -1
166    };
167  })();
168}
169
170if (!goog.asserts) {
171  goog.asserts = {
172    /**
173     * @param {*} condition Condition to check.
174     */
175    assert: function (condition) {
176      if (!condition) {
177        throw Error('Assertion error');
178      }
179    },
180    /**
181     * @param {...*} var_args
182     */
183    fail: function (var_args) {}
184  };
185}
186
187
188// Stub out the document wrapper used by renderAs*.
189if (!goog.dom) {
190  goog.dom = {};
191  /**
192   * @param {Document=} d
193   * @constructor
194   */
195  goog.dom.DomHelper = function(d) {
196    this.document_ = d || document;
197  };
198  /**
199   * @return {!Document}
200   */
201  goog.dom.DomHelper.prototype.getDocument = function() {
202    return this.document_;
203  };
204  /**
205   * Creates a new element.
206   * @param {string} name Tag name.
207   * @return {!Element}
208   */
209  goog.dom.DomHelper.prototype.createElement = function(name) {
210    return this.document_.createElement(name);
211  };
212  /**
213   * Creates a new document fragment.
214   * @return {!DocumentFragment}
215   */
216  goog.dom.DomHelper.prototype.createDocumentFragment = function() {
217    return this.document_.createDocumentFragment();
218  };
219}
220
221
222if (!goog.format) {
223  goog.format = {
224    insertWordBreaks: function(str, maxCharsBetweenWordBreaks) {
225      str = String(str);
226
227      var resultArr = [];
228      var resultArrLen = 0;
229
230      // These variables keep track of important state inside str.
231      var isInTag = false;  // whether we're inside an HTML tag
232      var isMaybeInEntity = false;  // whether we might be inside an HTML entity
233      var numCharsWithoutBreak = 0;  // number of chars since last word break
234      var flushIndex = 0;  // index of first char not yet flushed to resultArr
235
236      for (var i = 0, n = str.length; i < n; ++i) {
237        var charCode = str.charCodeAt(i);
238
239        // If hit maxCharsBetweenWordBreaks, and not space next, then add <wbr>.
240        if (numCharsWithoutBreak >= maxCharsBetweenWordBreaks &&
241            // space
242            charCode != 32) {
243          resultArr[resultArrLen++] = str.substring(flushIndex, i);
244          flushIndex = i;
245          resultArr[resultArrLen++] = goog.format.WORD_BREAK;
246          numCharsWithoutBreak = 0;
247        }
248
249        if (isInTag) {
250          // If inside an HTML tag and we see '>', it's the end of the tag.
251          if (charCode == 62) {
252            isInTag = false;
253          }
254
255        } else if (isMaybeInEntity) {
256          switch (charCode) {
257            // Inside an entity, a ';' is the end of the entity.
258            // The entity that just ended counts as one char, so increment
259            // numCharsWithoutBreak.
260          case 59:  // ';'
261            isMaybeInEntity = false;
262            ++numCharsWithoutBreak;
263            break;
264            // If maybe inside an entity and we see '<', we weren't actually in
265            // an entity. But now we're inside and HTML tag.
266          case 60:  // '<'
267            isMaybeInEntity = false;
268            isInTag = true;
269            break;
270            // If maybe inside an entity and we see ' ', we weren't actually in
271            // an entity. Just correct the state and reset the
272            // numCharsWithoutBreak since we just saw a space.
273          case 32:  // ' '
274            isMaybeInEntity = false;
275            numCharsWithoutBreak = 0;
276            break;
277          }
278
279        } else {  // !isInTag && !isInEntity
280          switch (charCode) {
281            // When not within a tag or an entity and we see '<', we're now
282            // inside an HTML tag.
283          case 60:  // '<'
284            isInTag = true;
285            break;
286            // When not within a tag or an entity and we see '&', we might be
287            // inside an entity.
288          case 38:  // '&'
289            isMaybeInEntity = true;
290            break;
291            // When we see a space, reset the numCharsWithoutBreak count.
292          case 32:  // ' '
293            numCharsWithoutBreak = 0;
294            break;
295            // When we see a non-space, increment the numCharsWithoutBreak.
296          default:
297            ++numCharsWithoutBreak;
298            break;
299          }
300        }
301      }
302
303      // Flush the remaining chars at the end of the string.
304      resultArr[resultArrLen++] = str.substring(flushIndex);
305
306      return resultArr.join('');
307    },
308    /**
309     * String inserted as a word break by insertWordBreaks(). Safari requires
310     * <wbr></wbr>, Opera needs the 'shy' entity, though this will give a
311     * visible hyphen at breaks. Other browsers just use <wbr>.
312     * @type {string}
313     * @private
314     */
315    WORD_BREAK: goog.userAgent.WEBKIT
316        ? '<wbr></wbr>' : goog.userAgent.OPERA ? '&shy;' : '<wbr>'
317  };
318}
319
320
321if (!goog.i18n) {
322  goog.i18n = {
323    bidi: {
324      /**
325       * Check the directionality of a piece of text, return true if the piece
326       * of text should be laid out in RTL direction.
327       * @param {string} text The piece of text that need to be detected.
328       * @param {boolean=} opt_isHtml Whether {@code text} is HTML/HTML-escaped.
329       *     Default: false.
330       * @return {boolean}
331       * @private
332       */
333      detectRtlDirectionality: function(text, opt_isHtml) {
334        text = soyshim.$$bidiStripHtmlIfNecessary_(text, opt_isHtml);
335        return soyshim.$$bidiRtlWordRatio_(text)
336            > soyshim.$$bidiRtlDetectionThreshold_;
337      }
338    }
339  };
340}
341
342/**
343 * Directionality enum.
344 * @enum {number}
345 */
346goog.i18n.bidi.Dir = {
347  RTL: -1,
348  UNKNOWN: 0,
349  LTR: 1
350};
351
352
353/**
354 * Convert a directionality given in various formats to a goog.i18n.bidi.Dir
355 * constant. Useful for interaction with different standards of directionality
356 * representation.
357 *
358 * @param {goog.i18n.bidi.Dir|number|boolean} givenDir Directionality given in
359 *     one of the following formats:
360 *     1. A goog.i18n.bidi.Dir constant.
361 *     2. A number (positive = LRT, negative = RTL, 0 = unknown).
362 *     3. A boolean (true = RTL, false = LTR).
363 * @return {goog.i18n.bidi.Dir} A goog.i18n.bidi.Dir constant matching the given
364 *     directionality.
365 */
366goog.i18n.bidi.toDir = function(givenDir) {
367  if (typeof givenDir == 'number') {
368    return givenDir > 0 ? goog.i18n.bidi.Dir.LTR :
369        givenDir < 0 ? goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.UNKNOWN;
370  } else {
371    return givenDir ? goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR;
372  }
373};
374
375
376/**
377 * Utility class for formatting text for display in a potentially
378 * opposite-directionality context without garbling. Provides the following
379 * functionality:
380 *
381 * @param {goog.i18n.bidi.Dir|number|boolean} dir The context
382 *     directionality as a number
383 *     (positive = LRT, negative = RTL, 0 = unknown).
384 * @constructor
385 */
386goog.i18n.BidiFormatter = function(dir) {
387  this.dir_ = goog.i18n.bidi.toDir(dir);
388};
389
390
391/**
392 * Returns 'dir="ltr"' or 'dir="rtl"', depending on {@code text}'s estimated
393 * directionality, if it is not the same as the context directionality.
394 * Otherwise, returns the empty string.
395 *
396 * @param {string} text Text whose directionality is to be estimated.
397 * @param {boolean=} opt_isHtml Whether {@code text} is HTML / HTML-escaped.
398 *     Default: false.
399 * @return {string} 'dir="rtl"' for RTL text in non-RTL context; 'dir="ltr"' for
400 *     LTR text in non-LTR context; else, the empty string.
401 */
402goog.i18n.BidiFormatter.prototype.dirAttr = function (text, opt_isHtml) {
403  var dir = soy.$$bidiTextDir(text, opt_isHtml);
404  return dir && dir != this.dir_ ? dir < 0 ? 'dir="rtl"' : 'dir="ltr"' : '';
405};
406
407/**
408 * Returns the trailing horizontal edge, i.e. "right" or "left", depending on
409 * the global bidi directionality.
410 * @return {string} "left" for RTL context and "right" otherwise.
411 */
412goog.i18n.BidiFormatter.prototype.endEdge = function () {
413  return this.dir_ < 0 ? 'left' : 'right';
414};
415
416/**
417 * Returns the Unicode BiDi mark matching the context directionality (LRM for
418 * LTR context directionality, RLM for RTL context directionality), or the
419 * empty string for neutral / unknown context directionality.
420 *
421 * @return {string} LRM for LTR context directionality and RLM for RTL context
422 *     directionality.
423 */
424goog.i18n.BidiFormatter.prototype.mark = function () {
425  return (
426      (this.dir_ > 0) ? '\u200E' /*LRM*/ :
427      (this.dir_ < 0) ? '\u200F' /*RLM*/ :
428      '');
429};
430
431/**
432 * Returns a Unicode BiDi mark matching the context directionality (LRM or RLM)
433 * if the directionality or the exit directionality of {@code text} are opposite
434 * to the context directionality. Otherwise returns the empty string.
435 *
436 * @param {string} text The input text.
437 * @param {boolean=} opt_isHtml Whether {@code text} is HTML / HTML-escaped.
438 *     Default: false.
439 * @return {string} A Unicode bidi mark matching the global directionality or
440 *     the empty string.
441 */
442goog.i18n.BidiFormatter.prototype.markAfter = function (text, opt_isHtml) {
443  var dir = soy.$$bidiTextDir(text, opt_isHtml);
444  return soyshim.$$bidiMarkAfterKnownDir_(this.dir_, dir, text, opt_isHtml);
445};
446
447/**
448 * Formats a string of unknown directionality for use in HTML output of the
449 * context directionality, so an opposite-directionality string is neither
450 * garbled nor garbles what follows it.
451 *
452 * @param {string} str The input text.
453 * @param {boolean=} placeholder This argument exists for consistency with the
454 *     Closure Library. Specifying it has no effect.
455 * @return {string} Input text after applying the above processing.
456 */
457goog.i18n.BidiFormatter.prototype.spanWrap = function(str, placeholder) {
458  str = String(str);
459  var textDir = soy.$$bidiTextDir(str, true);
460  var reset = soyshim.$$bidiMarkAfterKnownDir_(this.dir_, textDir, str, true);
461  if (textDir > 0 && this.dir_ <= 0) {
462    str = '<span dir="ltr">' + str + '</span>';
463  } else if (textDir < 0 && this.dir_ >= 0) {
464    str = '<span dir="rtl">' + str + '</span>';
465  }
466  return str + reset;
467};
468
469/**
470 * Returns the leading horizontal edge, i.e. "left" or "right", depending on
471 * the global bidi directionality.
472 * @return {string} "right" for RTL context and "left" otherwise.
473 */
474goog.i18n.BidiFormatter.prototype.startEdge = function () {
475  return this.dir_ < 0 ? 'right' : 'left';
476};
477
478/**
479 * Formats a string of unknown directionality for use in plain-text output of
480 * the context directionality, so an opposite-directionality string is neither
481 * garbled nor garbles what follows it.
482 * As opposed to {@link #spanWrap}, this makes use of unicode BiDi formatting
483 * characters. In HTML, its *only* valid use is inside of elements that do not
484 * allow mark-up, e.g. an 'option' tag.
485 *
486 * @param {string} str The input text.
487 * @param {boolean=} placeholder This argument exists for consistency with the
488 *     Closure Library. Specifying it has no effect.
489 * @return {string} Input text after applying the above processing.
490 */
491goog.i18n.BidiFormatter.prototype.unicodeWrap = function(str, placeholder) {
492  str = String(str);
493  var textDir = soy.$$bidiTextDir(str, true);
494  var reset = soyshim.$$bidiMarkAfterKnownDir_(this.dir_, textDir, str, true);
495  if (textDir > 0 && this.dir_ <= 0) {
496    str = '\u202A' + str + '\u202C';
497  } else if (textDir < 0 && this.dir_ >= 0) {
498    str = '\u202B' + str + '\u202C';
499  }
500  return str + reset;
501};
502
503
504goog.string = {
505
506  /**
507   * Converts \r\n, \r, and \n to <br>s
508   * @param {*} str The string in which to convert newlines.
509   * @param {boolean=} opt_xml Whether to use XML compatible tags.
510   * @return {string} A copy of {@code str} with converted newlines.
511   */
512  newLineToBr: function(str, opt_xml) {
513
514    str = String(str);
515
516    // This quick test helps in the case when there are no chars to replace,
517    // in the worst case this makes barely a difference to the time taken.
518    if (!goog.string.NEWLINE_TO_BR_RE_.test(str)) {
519      return str;
520    }
521
522    return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');
523  },
524  urlEncode: encodeURIComponent,
525  /**
526   * Regular expression used within newlineToBr().
527   * @type {RegExp}
528   * @private
529   */
530  NEWLINE_TO_BR_RE_: /[\r\n]/
531};
532
533
534/**
535 * Utility class to facilitate much faster string concatenation in IE,
536 * using Array.join() rather than the '+' operator. For other browsers
537 * we simply use the '+' operator.
538 *
539 * @param {Object|number|string|boolean=} opt_a1 Optional first initial item
540 *     to append.
541 * @param {...Object|number|string|boolean} var_args Other initial items to
542 *     append, e.g., new goog.string.StringBuffer('foo', 'bar').
543 * @constructor
544 */
545goog.string.StringBuffer = function(opt_a1, var_args) {
546  /**
547   * Internal buffer for the string to be concatenated.
548   * @type {string|Array}
549   * @private
550   */
551  this.buffer_ = goog.userAgent.jscript.HAS_JSCRIPT ? [] : '';
552
553  if (opt_a1 != null) {
554    this.append.apply(this, arguments);
555  }
556};
557
558
559/**
560 * Length of internal buffer (faster than calling buffer_.length).
561 * Only used for IE.
562 * @type {number}
563 * @private
564 */
565goog.string.StringBuffer.prototype.bufferLength_ = 0;
566
567/**
568 * Appends one or more items to the string.
569 *
570 * Calling this with null, undefined, or empty arguments is an error.
571 *
572 * @param {Object|number|string|boolean} a1 Required first string.
573 * @param {Object|number|string|boolean=} opt_a2 Optional second string.
574 * @param {...Object|number|string|boolean} var_args Other items to append,
575 *     e.g., sb.append('foo', 'bar', 'baz').
576 * @return {goog.string.StringBuffer} This same StringBuilder object.
577 */
578goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
579
580  if (goog.userAgent.jscript.HAS_JSCRIPT) {
581    if (opt_a2 == null) {  // no second argument (note: undefined == null)
582      // Array assignment is 2x faster than Array push. Also, use a1
583      // directly to avoid arguments instantiation, another 2x improvement.
584      this.buffer_[this.bufferLength_++] = a1;
585    } else {
586      var arr = /**@type {Array.<number|string|boolean>}*/(this.buffer_);
587      arr.push.apply(arr, arguments);
588      this.bufferLength_ = this.buffer_.length;
589    }
590
591  } else {
592
593    // Use a1 directly to avoid arguments instantiation for single-arg case.
594    this.buffer_ += a1;
595    if (opt_a2 != null) {  // no second argument (note: undefined == null)
596      for (var i = 1; i < arguments.length; i++) {
597        this.buffer_ += arguments[i];
598      }
599    }
600  }
601
602  return this;
603};
604
605
606/**
607 * Clears the string.
608 */
609goog.string.StringBuffer.prototype.clear = function() {
610
611  if (goog.userAgent.jscript.HAS_JSCRIPT) {
612     this.buffer_.length = 0;  // reuse array to avoid creating new object
613     this.bufferLength_ = 0;
614
615   } else {
616     this.buffer_ = '';
617   }
618};
619
620
621/**
622 * Returns the concatenated string.
623 *
624 * @return {string} The concatenated string.
625 */
626goog.string.StringBuffer.prototype.toString = function() {
627
628  if (goog.userAgent.jscript.HAS_JSCRIPT) {
629    var str = this.buffer_.join('');
630    // Given a string with the entire contents, simplify the StringBuilder by
631    // setting its contents to only be this string, rather than many fragments.
632    this.clear();
633    if (str) {
634      this.append(str);
635    }
636    return str;
637
638  } else {
639    return /** @type {string} */ (this.buffer_);
640  }
641};
642
643
644if (!goog.soy) goog.soy = {
645  /**
646   * Helper function to render a Soy template and then set the
647   * output string as the innerHTML of an element. It is recommended
648   * to use this helper function instead of directly setting
649   * innerHTML in your hand-written code, so that it will be easier
650   * to audit the code for cross-site scripting vulnerabilities.
651   *
652   * @param {Function} template The Soy template defining element's content.
653   * @param {Object=} opt_templateData The data for the template.
654   * @param {Object=} opt_injectedData The injected data for the template.
655   * @param {(goog.dom.DomHelper|Document)=} opt_dom The context in which DOM
656   *     nodes will be created.
657   */
658  renderAsElement: function(
659    template, opt_templateData, opt_injectedData, opt_dom) {
660    return /** @type {!Element} */ (soyshim.$$renderWithWrapper_(
661        template, opt_templateData, opt_dom, true /* asElement */,
662        opt_injectedData));
663  },
664  /**
665   * Helper function to render a Soy template into a single node or
666   * a document fragment. If the rendered HTML string represents a
667   * single node, then that node is returned (note that this is
668   * *not* a fragment, despite them name of the method). Otherwise a
669   * document fragment is returned containing the rendered nodes.
670   *
671   * @param {Function} template The Soy template defining element's content.
672   * @param {Object=} opt_templateData The data for the template.
673   * @param {Object=} opt_injectedData The injected data for the template.
674   * @param {(goog.dom.DomHelper|Document)=} opt_dom The context in which DOM
675   *     nodes will be created.
676   * @return {!Node} The resulting node or document fragment.
677   */
678  renderAsFragment: function(
679    template, opt_templateData, opt_injectedData, opt_dom) {
680    return soyshim.$$renderWithWrapper_(
681        template, opt_templateData, opt_dom, false /* asElement */,
682        opt_injectedData);
683  },
684  /**
685   * Helper function to render a Soy template and then set the output string as
686   * the innerHTML of an element. It is recommended to use this helper function
687   * instead of directly setting innerHTML in your hand-written code, so that it
688   * will be easier to audit the code for cross-site scripting vulnerabilities.
689   *
690   * NOTE: New code should consider using goog.soy.renderElement instead.
691   *
692   * @param {Element} element The element whose content we are rendering.
693   * @param {Function} template The Soy template defining the element's content.
694   * @param {Object=} opt_templateData The data for the template.
695   * @param {Object=} opt_injectedData The injected data for the template.
696   */
697  renderElement: function(
698      element, template, opt_templateData, opt_injectedData) {
699    element.innerHTML = template(opt_templateData, null, opt_injectedData);
700  },
701  data: {}
702};
703
704
705/**
706 * A type of textual content.
707 *
708 * This is an enum of type Object so that these values are unforgeable.
709 *
710 * @enum {!Object}
711 */
712goog.soy.data.SanitizedContentKind = {
713
714  /**
715   * A snippet of HTML that does not start or end inside a tag, comment, entity,
716   * or DOCTYPE; and that does not contain any executable code
717   * (JS, {@code <object>}s, etc.) from a different trust domain.
718   */
719  HTML: {},
720
721  /**
722   * Executable Javascript code or expression, safe for insertion in a
723   * script-tag or event handler context, known to be free of any
724   * attacker-controlled scripts. This can either be side-effect-free
725   * Javascript (such as JSON) or Javascript that entirely under Google's
726   * control.
727   */
728  JS: goog.DEBUG ? {sanitizedContentJsStrChars: true} : {},
729
730  /**
731   * A sequence of code units that can appear between quotes (either kind) in a
732   * JS program without causing a parse error, and without causing any side
733   * effects.
734   * <p>
735   * The content should not contain unescaped quotes, newlines, or anything else
736   * that would cause parsing to fail or to cause a JS parser to finish the
737   * string its parsing inside the content.
738   * <p>
739   * The content must also not end inside an escape sequence ; no partial octal
740   * escape sequences or odd number of '{@code \}'s at the end.
741   */
742  JS_STR_CHARS: {},
743
744  /** A properly encoded portion of a URI. */
745  URI: {},
746
747  /**
748   * Repeated attribute names and values. For example,
749   * {@code dir="ltr" foo="bar" onclick="trustedFunction()" checked}.
750   */
751  ATTRIBUTES: goog.DEBUG ? {sanitizedContentHtmlAttribute: true} : {},
752
753  // TODO: Consider separating rules, declarations, and values into
754  // separate types, but for simplicity, we'll treat explicitly blessed
755  // SanitizedContent as allowed in all of these contexts.
756  /**
757   * A CSS3 declaration, property, value or group of semicolon separated
758   * declarations.
759   */
760  CSS: {},
761
762  /**
763   * Unsanitized plain-text content.
764   *
765   * This is effectively the "null" entry of this enum, and is sometimes used
766   * to explicitly mark content that should never be used unescaped. Since any
767   * string is safe to use as text, being of ContentKind.TEXT makes no
768   * guarantees about its safety in any other context such as HTML.
769   */
770  TEXT: {}
771};
772
773
774
775/**
776 * A string-like object that carries a content-type.
777 *
778 * IMPORTANT! Do not create these directly, nor instantiate the subclasses.
779 * Instead, use a trusted, centrally reviewed library as endorsed by your team
780 * to generate these objects. Otherwise, you risk accidentally creating
781 * SanitizedContent that is attacker-controlled and gets evaluated unescaped in
782 * templates.
783 *
784 * @constructor
785 */
786goog.soy.data.SanitizedContent = function() {
787  throw Error('Do not instantiate directly');
788};
789
790
791/**
792 * The context in which this content is safe from XSS attacks.
793 * @type {goog.soy.data.SanitizedContentKind}
794 */
795goog.soy.data.SanitizedContent.prototype.contentKind;
796
797
798/**
799 * The already-safe content.
800 * @type {string}
801 */
802goog.soy.data.SanitizedContent.prototype.content;
803
804
805/** @override */
806goog.soy.data.SanitizedContent.prototype.toString = function() {
807  return this.content;
808};
809
810
811var soy = { esc: {} };
812var soydata = {};
813soydata.VERY_UNSAFE = {};
814var soyshim = { $$DEFAULT_TEMPLATE_DATA_: {} };
815/**
816 * Helper function to render a Soy template into a single node or a document
817 * fragment. If the rendered HTML string represents a single node, then that
818 * node is returned. Otherwise a document fragment is created and returned
819 * (wrapped in a DIV element if #opt_singleNode is true).
820 *
821 * @param {Function} template The Soy template defining the element's content.
822 * @param {Object=} opt_templateData The data for the template.
823 * @param {(goog.dom.DomHelper|Document)=} opt_dom The context in which DOM
824 *     nodes will be created.
825 * @param {boolean=} opt_asElement Whether to wrap the fragment in an
826 *     element if the template does not render a single element. If true,
827 *     result is always an Element.
828 * @param {Object=} opt_injectedData The injected data for the template.
829 * @return {!Node} The resulting node or document fragment.
830 * @private
831 */
832soyshim.$$renderWithWrapper_ = function(
833    template, opt_templateData, opt_dom, opt_asElement, opt_injectedData) {
834
835  var dom = opt_dom || document;
836  var wrapper = dom.createElement('div');
837  var templateContent = template(
838  opt_templateData || soyshim.$$DEFAULT_TEMPLATE_DATA_, undefined,
839    opt_injectedData);
840  while (templateContent.firstChild) {
841    wrapper.appendChild(templateContent.firstChild);
842  }
843  // If the template renders as a single element, return it.
844  if (wrapper.childNodes.length == 1) {
845    var firstChild = wrapper.firstChild;
846    if (!opt_asElement || firstChild.nodeType == 1 /* Element */) {
847      return /** @type {!Node} */ (firstChild);
848    }
849  }
850
851  // If we're forcing it to be a single element, return the wrapper DIV.
852  if (opt_asElement) {
853    return wrapper;
854  }
855
856  // Otherwise, create and return a fragment.
857  var fragment = dom.createDocumentFragment();
858  while (wrapper.firstChild) {
859    fragment.appendChild(wrapper.firstChild);
860  }
861  return fragment;
862};
863
864
865/**
866 * Returns a Unicode BiDi mark matching bidiGlobalDir (LRM or RLM) if the
867 * directionality or the exit directionality of text are opposite to
868 * bidiGlobalDir. Otherwise returns the empty string.
869 * If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes
870 * in text, making the logic suitable for HTML and HTML-escaped text.
871 * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
872 *     if rtl, 0 if unknown.
873 * @param {number} dir text's directionality: 1 if ltr, -1 if rtl, 0 if unknown.
874 * @param {string} text The text whose directionality is to be estimated.
875 * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
876 *     Default: false.
877 * @return {string} A Unicode bidi mark matching bidiGlobalDir, or
878 *     the empty string when text's overall and exit directionalities both match
879 *     bidiGlobalDir, or bidiGlobalDir is 0 (unknown).
880 * @private
881 */
882soyshim.$$bidiMarkAfterKnownDir_ = function(
883    bidiGlobalDir, dir, text, opt_isHtml) {
884  return (
885      bidiGlobalDir > 0 && (dir < 0 ||
886          soyshim.$$bidiIsRtlExitText_(text, opt_isHtml)) ? '\u200E' : // LRM
887      bidiGlobalDir < 0 && (dir > 0 ||
888          soyshim.$$bidiIsLtrExitText_(text, opt_isHtml)) ? '\u200F' : // RLM
889      '');
890};
891
892
893/**
894 * Strips str of any HTML mark-up and escapes. Imprecise in several ways, but
895 * precision is not very important, since the result is only meant to be used
896 * for directionality detection.
897 * @param {string} str The string to be stripped.
898 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
899 *     Default: false.
900 * @return {string} The stripped string.
901 * @private
902 */
903soyshim.$$bidiStripHtmlIfNecessary_ = function(str, opt_isHtml) {
904  return opt_isHtml ? str.replace(soyshim.$$BIDI_HTML_SKIP_RE_, ' ') : str;
905};
906
907
908/**
909 * Simplified regular expression for am HTML tag (opening or closing) or an HTML
910 * escape - the things we want to skip over in order to ignore their ltr
911 * characters.
912 * @type {RegExp}
913 * @private
914 */
915soyshim.$$BIDI_HTML_SKIP_RE_ = /<[^>]*>|&[^;]+;/g;
916
917
918/**
919 * A practical pattern to identify strong LTR character. This pattern is not
920 * theoretically correct according to unicode standard. It is simplified for
921 * performance and small code size.
922 * @type {string}
923 * @private
924 */
925soyshim.$$bidiLtrChars_ =
926    'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF' +
927    '\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF';
928
929
930/**
931 * A practical pattern to identify strong neutral and weak character. This
932 * pattern is not theoretically correct according to unicode standard. It is
933 * simplified for performance and small code size.
934 * @type {string}
935 * @private
936 */
937soyshim.$$bidiNeutralChars_ =
938    '\u0000-\u0020!-@[-`{-\u00BF\u00D7\u00F7\u02B9-\u02FF\u2000-\u2BFF';
939
940
941/**
942 * A practical pattern to identify strong RTL character. This pattern is not
943 * theoretically correct according to unicode standard. It is simplified for
944 * performance and small code size.
945 * @type {string}
946 * @private
947 */
948soyshim.$$bidiRtlChars_ = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC';
949
950
951/**
952 * Regular expressions to check if a piece of text is of RTL directionality
953 * on first character with strong directionality.
954 * @type {RegExp}
955 * @private
956 */
957soyshim.$$bidiRtlDirCheckRe_ = new RegExp(
958    '^[^' + soyshim.$$bidiLtrChars_ + ']*[' + soyshim.$$bidiRtlChars_ + ']');
959
960
961/**
962 * Regular expressions to check if a piece of text is of neutral directionality.
963 * Url are considered as neutral.
964 * @type {RegExp}
965 * @private
966 */
967soyshim.$$bidiNeutralDirCheckRe_ = new RegExp(
968    '^[' + soyshim.$$bidiNeutralChars_ + ']*$|^http://');
969
970
971/**
972 * Check the directionality of the a piece of text based on the first character
973 * with strong directionality.
974 * @param {string} str string being checked.
975 * @return {boolean} return true if rtl directionality is being detected.
976 * @private
977 */
978soyshim.$$bidiIsRtlText_ = function(str) {
979  return soyshim.$$bidiRtlDirCheckRe_.test(str);
980};
981
982
983/**
984 * Check the directionality of the a piece of text based on the first character
985 * with strong directionality.
986 * @param {string} str string being checked.
987 * @return {boolean} true if all characters have neutral directionality.
988 * @private
989 */
990soyshim.$$bidiIsNeutralText_ = function(str) {
991  return soyshim.$$bidiNeutralDirCheckRe_.test(str);
992};
993
994
995/**
996 * This constant controls threshold of rtl directionality.
997 * @type {number}
998 * @private
999 */
1000soyshim.$$bidiRtlDetectionThreshold_ = 0.40;
1001
1002
1003/**
1004 * Returns the RTL ratio based on word count.
1005 * @param {string} str the string that need to be checked.
1006 * @return {number} the ratio of RTL words among all words with directionality.
1007 * @private
1008 */
1009soyshim.$$bidiRtlWordRatio_ = function(str) {
1010  var rtlCount = 0;
1011  var totalCount = 0;
1012  var tokens = str.split(' ');
1013  for (var i = 0; i < tokens.length; i++) {
1014    if (soyshim.$$bidiIsRtlText_(tokens[i])) {
1015      rtlCount++;
1016      totalCount++;
1017    } else if (!soyshim.$$bidiIsNeutralText_(tokens[i])) {
1018      totalCount++;
1019    }
1020  }
1021
1022  return totalCount == 0 ? 0 : rtlCount / totalCount;
1023};
1024
1025
1026/**
1027 * Regular expressions to check if the last strongly-directional character in a
1028 * piece of text is LTR.
1029 * @type {RegExp}
1030 * @private
1031 */
1032soyshim.$$bidiLtrExitDirCheckRe_ = new RegExp(
1033    '[' + soyshim.$$bidiLtrChars_ + '][^' + soyshim.$$bidiRtlChars_ + ']*$');
1034
1035
1036/**
1037 * Regular expressions to check if the last strongly-directional character in a
1038 * piece of text is RTL.
1039 * @type {RegExp}
1040 * @private
1041 */
1042soyshim.$$bidiRtlExitDirCheckRe_ = new RegExp(
1043    '[' + soyshim.$$bidiRtlChars_ + '][^' + soyshim.$$bidiLtrChars_ + ']*$');
1044
1045
1046/**
1047 * Check if the exit directionality a piece of text is LTR, i.e. if the last
1048 * strongly-directional character in the string is LTR.
1049 * @param {string} str string being checked.
1050 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
1051 *     Default: false.
1052 * @return {boolean} Whether LTR exit directionality was detected.
1053 * @private
1054 */
1055soyshim.$$bidiIsLtrExitText_ = function(str, opt_isHtml) {
1056  str = soyshim.$$bidiStripHtmlIfNecessary_(str, opt_isHtml);
1057  return soyshim.$$bidiLtrExitDirCheckRe_.test(str);
1058};
1059
1060
1061/**
1062 * Check if the exit directionality a piece of text is RTL, i.e. if the last
1063 * strongly-directional character in the string is RTL.
1064 * @param {string} str string being checked.
1065 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
1066 *     Default: false.
1067 * @return {boolean} Whether RTL exit directionality was detected.
1068 * @private
1069 */
1070soyshim.$$bidiIsRtlExitText_ = function(str, opt_isHtml) {
1071  str = soyshim.$$bidiStripHtmlIfNecessary_(str, opt_isHtml);
1072  return soyshim.$$bidiRtlExitDirCheckRe_.test(str);
1073};
1074
1075
1076// =============================================================================
1077// COPIED FROM soyutils_usegoog.js
1078
1079
1080// -----------------------------------------------------------------------------
1081// StringBuilder (compatible with the 'stringbuilder' code style).
1082
1083
1084/**
1085 * Utility class to facilitate much faster string concatenation in IE,
1086 * using Array.join() rather than the '+' operator. For other browsers
1087 * we simply use the '+' operator.
1088 *
1089 * @param {Object} var_args Initial items to append,
1090 *     e.g., new soy.StringBuilder('foo', 'bar').
1091 * @constructor
1092 */
1093soy.StringBuilder = goog.string.StringBuffer;
1094
1095
1096// -----------------------------------------------------------------------------
1097// soydata: Defines typed strings, e.g. an HTML string {@code "a<b>c"} is
1098// semantically distinct from the plain text string {@code "a<b>c"} and smart
1099// templates can take that distinction into account.
1100
1101/**
1102 * A type of textual content.
1103 *
1104 * This is an enum of type Object so that these values are unforgeable.
1105 *
1106 * @enum {!Object}
1107 */
1108soydata.SanitizedContentKind = goog.soy.data.SanitizedContentKind;
1109
1110
1111/**
1112 * Content of type {@link soydata.SanitizedContentKind.HTML}.
1113 *
1114 * The content is a string of HTML that can safely be embedded in a PCDATA
1115 * context in your app.  If you would be surprised to find that an HTML
1116 * sanitizer produced {@code s} (e.g.  it runs code or fetches bad URLs) and
1117 * you wouldn't write a template that produces {@code s} on security or privacy
1118 * grounds, then don't pass {@code s} here.
1119 *
1120 * @constructor
1121 * @extends {goog.soy.data.SanitizedContent}
1122 */
1123soydata.SanitizedHtml = function() {
1124  goog.soy.data.SanitizedContent.call(this);  // Throws an exception.
1125};
1126goog.inherits(soydata.SanitizedHtml, goog.soy.data.SanitizedContent);
1127
1128/** @override */
1129soydata.SanitizedHtml.prototype.contentKind = soydata.SanitizedContentKind.HTML;
1130
1131
1132/**
1133 * Content of type {@link soydata.SanitizedContentKind.JS}.
1134 *
1135 * The content is Javascript source that when evaluated does not execute any
1136 * attacker-controlled scripts.
1137 *
1138 * @constructor
1139 * @extends {goog.soy.data.SanitizedContent}
1140 */
1141soydata.SanitizedJs = function() {
1142  goog.soy.data.SanitizedContent.call(this);  // Throws an exception.
1143};
1144goog.inherits(soydata.SanitizedJs, goog.soy.data.SanitizedContent);
1145
1146/** @override */
1147soydata.SanitizedJs.prototype.contentKind =
1148    soydata.SanitizedContentKind.JS;
1149
1150
1151/**
1152 * Content of type {@link soydata.SanitizedContentKind.JS_STR_CHARS}.
1153 *
1154 * The content can be safely inserted as part of a single- or double-quoted
1155 * string without terminating the string.
1156 *
1157 * @constructor
1158 * @extends {goog.soy.data.SanitizedContent}
1159 */
1160soydata.SanitizedJsStrChars = function() {
1161  goog.soy.data.SanitizedContent.call(this);  // Throws an exception.
1162};
1163goog.inherits(soydata.SanitizedJsStrChars, goog.soy.data.SanitizedContent);
1164
1165/** @override */
1166soydata.SanitizedJsStrChars.prototype.contentKind =
1167    soydata.SanitizedContentKind.JS_STR_CHARS;
1168
1169
1170/**
1171 * Content of type {@link soydata.SanitizedContentKind.URI}.
1172 *
1173 * The content is a URI chunk that the caller knows is safe to emit in a
1174 * template.
1175 *
1176 * @constructor
1177 * @extends {goog.soy.data.SanitizedContent}
1178 */
1179soydata.SanitizedUri = function() {
1180  goog.soy.data.SanitizedContent.call(this);  // Throws an exception.
1181};
1182goog.inherits(soydata.SanitizedUri, goog.soy.data.SanitizedContent);
1183
1184/** @override */
1185soydata.SanitizedUri.prototype.contentKind = soydata.SanitizedContentKind.URI;
1186
1187
1188/**
1189 * Content of type {@link soydata.SanitizedContentKind.ATTRIBUTES}.
1190 *
1191 * The content should be safely embeddable within an open tag, such as a
1192 * key="value" pair.
1193 *
1194 * @constructor
1195 * @extends {goog.soy.data.SanitizedContent}
1196 */
1197soydata.SanitizedHtmlAttribute = function() {
1198  goog.soy.data.SanitizedContent.call(this);  // Throws an exception.
1199};
1200goog.inherits(soydata.SanitizedHtmlAttribute, goog.soy.data.SanitizedContent);
1201
1202/** @override */
1203soydata.SanitizedHtmlAttribute.prototype.contentKind =
1204    soydata.SanitizedContentKind.ATTRIBUTES;
1205
1206
1207/**
1208 * Content of type {@link soydata.SanitizedContentKind.CSS}.
1209 *
1210 * The content is non-attacker-exploitable CSS, such as {@code color:#c3d9ff}.
1211 *
1212 * @constructor
1213 * @extends {goog.soy.data.SanitizedContent}
1214 */
1215soydata.SanitizedCss = function() {
1216  goog.soy.data.SanitizedContent.call(this);  // Throws an exception.
1217};
1218goog.inherits(soydata.SanitizedCss, goog.soy.data.SanitizedContent);
1219
1220/** @override */
1221soydata.SanitizedCss.prototype.contentKind =
1222    soydata.SanitizedContentKind.CSS;
1223
1224
1225/**
1226 * Unsanitized plain text string.
1227 *
1228 * While all strings are effectively safe to use as a plain text, there are no
1229 * guarantees about safety in any other context such as HTML. This is
1230 * sometimes used to mark that should never be used unescaped.
1231 *
1232 * @param {*} content Plain text with no guarantees.
1233 * @constructor
1234 * @extends {goog.soy.data.SanitizedContent}
1235 */
1236soydata.UnsanitizedText = function(content) {
1237  /** @override */
1238  this.content = String(content);
1239};
1240goog.inherits(soydata.UnsanitizedText, goog.soy.data.SanitizedContent);
1241
1242/** @override */
1243soydata.UnsanitizedText.prototype.contentKind =
1244    soydata.SanitizedContentKind.TEXT;
1245
1246
1247/**
1248 * Creates a factory for SanitizedContent types.
1249 *
1250 * This is a hack so that the soydata.VERY_UNSAFE.ordainSanitized* can
1251 * instantiate Sanitized* classes, without making the Sanitized* constructors
1252 * publicly usable. Requiring all construction to use the VERY_UNSAFE names
1253 * helps callers and their reviewers easily tell that creating SanitizedContent
1254 * is not always safe and calls for careful review.
1255 *
1256 * @param {function(new: T, string)} ctor A constructor.
1257 * @return {!function(*): T} A factory that takes content and returns a
1258 *     new instance.
1259 * @template T
1260 * @private
1261 */
1262soydata.$$makeSanitizedContentFactory_ = function(ctor) {
1263  /** @constructor */
1264  function InstantiableCtor() {}
1265  InstantiableCtor.prototype = ctor.prototype;
1266  return function(content) {
1267    var result = new InstantiableCtor();
1268    result.content = String(content);
1269    return result;
1270  };
1271};
1272
1273
1274// -----------------------------------------------------------------------------
1275// Sanitized content ordainers. Please use these with extreme caution (with the
1276// exception of markUnsanitizedText). A good recommendation is to limit usage
1277// of these to just a handful of files in your source tree where usages can be
1278// carefully audited.
1279
1280
1281/**
1282 * Protects a string from being used in an noAutoescaped context.
1283 *
1284 * This is useful for content where there is significant risk of accidental
1285 * unescaped usage in a Soy template. A great case is for user-controlled
1286 * data that has historically been a source of vulernabilities.
1287 *
1288 * @param {*} content Text to protect.
1289 * @return {!soydata.UnsanitizedText} A wrapper that is rejected by the
1290 *     Soy noAutoescape print directive.
1291 */
1292soydata.markUnsanitizedText = function(content) {
1293  return new soydata.UnsanitizedText(content);
1294};
1295
1296
1297/**
1298 * Takes a leap of faith that the provided content is "safe" HTML.
1299 *
1300 * @param {*} content A string of HTML that can safely be embedded in
1301 *     a PCDATA context in your app. If you would be surprised to find that an
1302 *     HTML sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs)
1303 *     and you wouldn't write a template that produces {@code s} on security or
1304 *     privacy grounds, then don't pass {@code s} here.
1305 * @return {!soydata.SanitizedHtml} Sanitized content wrapper that
1306 *     indicates to Soy not to escape when printed as HTML.
1307 */
1308soydata.VERY_UNSAFE.ordainSanitizedHtml =
1309    soydata.$$makeSanitizedContentFactory_(soydata.SanitizedHtml);
1310
1311
1312/**
1313 * Takes a leap of faith that the provided content is "safe" (non-attacker-
1314 * controlled, XSS-free) Javascript.
1315 *
1316 * @param {*} content Javascript source that when evaluated does not
1317 *     execute any attacker-controlled scripts.
1318 * @return {!soydata.SanitizedJs} Sanitized content wrapper that indicates to
1319 *     Soy not to escape when printed as Javascript source.
1320 */
1321soydata.VERY_UNSAFE.ordainSanitizedJs =
1322    soydata.$$makeSanitizedContentFactory_(soydata.SanitizedJs);
1323
1324
1325// TODO: This function is probably necessary, either externally or internally
1326// as an implementation detail. Generally, plain text will always work here,
1327// as there's no harm to unescaping the string and then re-escaping when
1328// finally printed.
1329/**
1330 * Takes a leap of faith that the provided content can be safely embedded in
1331 * a Javascript string without re-esacping.
1332 *
1333 * @param {*} content Content that can be safely inserted as part of a
1334 *     single- or double-quoted string without terminating the string.
1335 * @return {!soydata.SanitizedJsStrChars} Sanitized content wrapper that
1336 *     indicates to Soy not to escape when printed in a JS string.
1337 */
1338soydata.VERY_UNSAFE.ordainSanitizedJsStrChars =
1339    soydata.$$makeSanitizedContentFactory_(soydata.SanitizedJsStrChars);
1340
1341
1342/**
1343 * Takes a leap of faith that the provided content is "safe" to use as a URI
1344 * in a Soy template.
1345 *
1346 * This creates a Soy SanitizedContent object which indicates to Soy there is
1347 * no need to escape it when printed as a URI (e.g. in an href or src
1348 * attribute), such as if it's already been encoded or  if it's a Javascript:
1349 * URI.
1350 *
1351 * @param {*} content A chunk of URI that the caller knows is safe to
1352 *     emit in a template.
1353 * @return {!soydata.SanitizedUri} Sanitized content wrapper that indicates to
1354 *     Soy not to escape or filter when printed in URI context.
1355 */
1356soydata.VERY_UNSAFE.ordainSanitizedUri =
1357    soydata.$$makeSanitizedContentFactory_(soydata.SanitizedUri);
1358
1359
1360/**
1361 * Takes a leap of faith that the provided content is "safe" to use as an
1362 * HTML attribute.
1363 *
1364 * @param {*} content An attribute name and value, such as
1365 *     {@code dir="ltr"}.
1366 * @return {!soydata.SanitizedHtmlAttribute} Sanitized content wrapper that
1367 *     indicates to Soy not to escape when printed as an HTML attribute.
1368 */
1369soydata.VERY_UNSAFE.ordainSanitizedHtmlAttribute =
1370    soydata.$$makeSanitizedContentFactory_(soydata.SanitizedHtmlAttribute);
1371
1372
1373/**
1374 * Takes a leap of faith that the provided content is "safe" to use as CSS
1375 * in a style attribute or block.
1376 *
1377 * @param {*} content CSS, such as {@code color:#c3d9ff}.
1378 * @return {!soydata.SanitizedCss} Sanitized CSS wrapper that indicates to
1379 *     Soy there is no need to escape or filter when printed in CSS context.
1380 */
1381soydata.VERY_UNSAFE.ordainSanitizedCss =
1382    soydata.$$makeSanitizedContentFactory_(soydata.SanitizedCss);
1383
1384
1385// -----------------------------------------------------------------------------
1386// Public utilities.
1387
1388
1389/**
1390 * Helper function to render a Soy template and then set the output string as
1391 * the innerHTML of an element. It is recommended to use this helper function
1392 * instead of directly setting innerHTML in your hand-written code, so that it
1393 * will be easier to audit the code for cross-site scripting vulnerabilities.
1394 *
1395 * NOTE: New code should consider using goog.soy.renderElement instead.
1396 *
1397 * @param {Element} element The element whose content we are rendering.
1398 * @param {Function} template The Soy template defining the element's content.
1399 * @param {Object=} opt_templateData The data for the template.
1400 * @param {Object=} opt_injectedData The injected data for the template.
1401 */
1402soy.renderElement = goog.soy.renderElement;
1403
1404
1405/**
1406 * Helper function to render a Soy template into a single node or a document
1407 * fragment. If the rendered HTML string represents a single node, then that
1408 * node is returned (note that this is *not* a fragment, despite them name of
1409 * the method). Otherwise a document fragment is returned containing the
1410 * rendered nodes.
1411 *
1412 * NOTE: New code should consider using goog.soy.renderAsFragment
1413 * instead (note that the arguments are different).
1414 *
1415 * @param {Function} template The Soy template defining the element's content.
1416 * @param {Object=} opt_templateData The data for the template.
1417 * @param {Document=} opt_document The document used to create DOM nodes. If not
1418 *     specified, global document object is used.
1419 * @param {Object=} opt_injectedData The injected data for the template.
1420 * @return {!Node} The resulting node or document fragment.
1421 */
1422soy.renderAsFragment = function(
1423    template, opt_templateData, opt_document, opt_injectedData) {
1424  return goog.soy.renderAsFragment(
1425      template, opt_templateData, opt_injectedData,
1426      new goog.dom.DomHelper(opt_document));
1427};
1428
1429
1430/**
1431 * Helper function to render a Soy template into a single node. If the rendered
1432 * HTML string represents a single node, then that node is returned. Otherwise,
1433 * a DIV element is returned containing the rendered nodes.
1434 *
1435 * NOTE: New code should consider using goog.soy.renderAsElement
1436 * instead (note that the arguments are different).
1437 *
1438 * @param {Function} template The Soy template defining the element's content.
1439 * @param {Object=} opt_templateData The data for the template.
1440 * @param {Document=} opt_document The document used to create DOM nodes. If not
1441 *     specified, global document object is used.
1442 * @param {Object=} opt_injectedData The injected data for the template.
1443 * @return {!Element} Rendered template contents, wrapped in a parent DIV
1444 *     element if necessary.
1445 */
1446soy.renderAsElement = function(
1447    template, opt_templateData, opt_document, opt_injectedData) {
1448  return goog.soy.renderAsElement(
1449      template, opt_templateData, opt_injectedData,
1450      new goog.dom.DomHelper(opt_document));
1451};
1452
1453
1454// -----------------------------------------------------------------------------
1455// Below are private utilities to be used by Soy-generated code only.
1456
1457
1458/**
1459 * Builds an augmented map. The returned map will contain mappings from both
1460 * the base map and the additional map. If the same key appears in both, then
1461 * the value from the additional map will be visible, while the value from the
1462 * base map will be hidden. The base map will be used, but not modified.
1463 *
1464 * @param {!Object} baseMap The original map to augment.
1465 * @param {!Object} additionalMap A map containing the additional mappings.
1466 * @return {!Object} An augmented map containing both the original and
1467 *     additional mappings.
1468 */
1469soy.$$augmentMap = function(baseMap, additionalMap) {
1470
1471  // Create a new map whose '__proto__' field is set to baseMap.
1472  /** @constructor */
1473  function TempCtor() {}
1474  TempCtor.prototype = baseMap;
1475  var augmentedMap = new TempCtor();
1476
1477  // Add the additional mappings to the new map.
1478  for (var key in additionalMap) {
1479    augmentedMap[key] = additionalMap[key];
1480  }
1481
1482  return augmentedMap;
1483};
1484
1485
1486/**
1487 * Checks that the given map key is a string.
1488 * @param {*} key Key to check.
1489 * @return {string} The given key.
1490 */
1491soy.$$checkMapKey = function(key) {
1492  if ((typeof key) != 'string') {
1493    throw Error(
1494        'Map literal\'s key expression must evaluate to string' +
1495        ' (encountered type "' + (typeof key) + '").');
1496  }
1497  return key;
1498};
1499
1500
1501/**
1502 * Gets the keys in a map as an array. There are no guarantees on the order.
1503 * @param {Object} map The map to get the keys of.
1504 * @return {Array.<string>} The array of keys in the given map.
1505 */
1506soy.$$getMapKeys = function(map) {
1507  var mapKeys = [];
1508  for (var key in map) {
1509    mapKeys.push(key);
1510  }
1511  return mapKeys;
1512};
1513
1514
1515/**
1516 * Gets a consistent unique id for the given delegate template name. Two calls
1517 * to this function will return the same id if and only if the input names are
1518 * the same.
1519 *
1520 * <p> Important: This function must always be called with a string constant.
1521 *
1522 * <p> If Closure Compiler is not being used, then this is just this identity
1523 * function. If Closure Compiler is being used, then each call to this function
1524 * will be replaced with a short string constant, which will be consistent per
1525 * input name.
1526 *
1527 * @param {string} delTemplateName The delegate template name for which to get a
1528 *     consistent unique id.
1529 * @return {string} A unique id that is consistent per input name.
1530 *
1531 * @consistentIdGenerator
1532 */
1533soy.$$getDelTemplateId = function(delTemplateName) {
1534  return delTemplateName;
1535};
1536
1537
1538/**
1539 * Map from registered delegate template key to the priority of the
1540 * implementation.
1541 * @type {Object}
1542 * @private
1543 */
1544soy.$$DELEGATE_REGISTRY_PRIORITIES_ = {};
1545
1546/**
1547 * Map from registered delegate template key to the implementation function.
1548 * @type {Object}
1549 * @private
1550 */
1551soy.$$DELEGATE_REGISTRY_FUNCTIONS_ = {};
1552
1553
1554/**
1555 * Registers a delegate implementation. If the same delegate template key (id
1556 * and variant) has been registered previously, then priority values are
1557 * compared and only the higher priority implementation is stored (if
1558 * priorities are equal, an error is thrown).
1559 *
1560 * @param {string} delTemplateId The delegate template id.
1561 * @param {string} delTemplateVariant The delegate template variant (can be
1562 *     empty string).
1563 * @param {number} delPriority The implementation's priority value.
1564 * @param {Function} delFn The implementation function.
1565 */
1566soy.$$registerDelegateFn = function(
1567    delTemplateId, delTemplateVariant, delPriority, delFn) {
1568
1569  var mapKey = 'key_' + delTemplateId + ':' + delTemplateVariant;
1570  var currPriority = soy.$$DELEGATE_REGISTRY_PRIORITIES_[mapKey];
1571  if (currPriority === undefined || delPriority > currPriority) {
1572    // Registering new or higher-priority function: replace registry entry.
1573    soy.$$DELEGATE_REGISTRY_PRIORITIES_[mapKey] = delPriority;
1574    soy.$$DELEGATE_REGISTRY_FUNCTIONS_[mapKey] = delFn;
1575  } else if (delPriority == currPriority) {
1576    // Registering same-priority function: error.
1577    throw Error(
1578        'Encountered two active delegates with the same priority ("' +
1579            delTemplateId + ':' + delTemplateVariant + '").');
1580  } else {
1581    // Registering lower-priority function: do nothing.
1582  }
1583};
1584
1585
1586/**
1587 * Retrieves the (highest-priority) implementation that has been registered for
1588 * a given delegate template key (id and variant). If no implementation has
1589 * been registered for the key, then the fallback is the same id with empty
1590 * variant. If the fallback is also not registered, and allowsEmptyDefault is
1591 * true, then returns an implementation that is equivalent to an empty template
1592 * (i.e. rendered output would be empty string).
1593 *
1594 * @param {string} delTemplateId The delegate template id.
1595 * @param {string} delTemplateVariant The delegate template variant (can be
1596 *     empty string).
1597 * @param {boolean} allowsEmptyDefault Whether to default to the empty template
1598 *     function if there's no active implementation.
1599 * @return {Function}
1599 The retrieved implementation function.
1600 */
1601soy.$$getDelegateFn = function(
1602    delTemplateId, delTemplateVariant, allowsEmptyDefault) {
1603
1604  var delFn = soy.$$DELEGATE_REGISTRY_FUNCTIONS_[
1605      'key_' + delTemplateId + ':' + delTemplateVariant];
1606  if (! delFn && delTemplateVariant != '') {
1607    // Fallback to empty variant.
1608    delFn = soy.$$DELEGATE_REGISTRY_FUNCTIONS_['key_' + delTemplateId + ':'];
1609  }
1610
1611  if (delFn) {
1612    return delFn;
1613  } else if (allowsEmptyDefault) {
1614    return soy.$$EMPTY_TEMPLATE_FN_;
1615  } else {
1616    throw Error(
1617        'Found no active impl for delegate call to "' + delTemplateId + ':' +
1618            delTemplateVariant + '" (and not allowemptydefault="true").');
1619  }
1620};
1621
1622
1623/**
1624 * Private helper soy.$$getDelegateFn(). This is the empty template function
1625 * that is returned whenever there's no delegate implementation found.
1626 *
1627 * @param {Object.<string, *>=} opt_data
1628 * @param {soy.StringBuilder=} opt_sb
1629 * @param {Object.<string, *>=} opt_ijData
1630 * @return {string}
1631 * @private
1632 */
1633soy.$$EMPTY_TEMPLATE_FN_ = function(opt_data, opt_sb, opt_ijData) {
1634  return '';
1635};
1636
1637
1638// -----------------------------------------------------------------------------
1639// Escape/filter/normalize.
1640
1641
1642/**
1643 * Escapes HTML special characters in a string. Escapes double quote '"' in
1644 * addition to '&', '<', and '>' so that a string can be included in an HTML
1645 * tag attribute value within double quotes.
1646 * Will emit known safe HTML as-is.
1647 *
1648 * @param {*} value The string-like value to be escaped. May not be a string,
1649 *     but the value will be coerced to a string.
1650 * @return {string} An escaped version of value.
1651 */
1652soy.$$escapeHtml = function(value) {
1653  // TODO: Perhaps we should just ignore the contentKind property and instead
1654  // look only at the constructor.
1655  if (value && value.contentKind &&
1656      value.contentKind === goog.soy.data.SanitizedContentKind.HTML) {
1657    goog.asserts.assert(
1658        value.constructor === soydata.SanitizedHtml);
1659    return value.content;
1660  }
1661  return soy.esc.$$escapeHtmlHelper(value);
1662};
1663
1664
1665/**
1666 * Strips unsafe tags to convert a string of untrusted HTML into HTML that
1667 * is safe to embed.
1668 *
1669 * @param {*} value The string-like value to be escaped. May not be a string,
1670 *     but the value will be coerced to a string.
1671 * @return {string} A sanitized and normalized version of value.
1672 */
1673soy.$$cleanHtml = function(value) {
1674  if (value && value.contentKind &&
1675      value.contentKind === goog.soy.data.SanitizedContentKind.HTML) {
1676    goog.asserts.assert(
1677        value.constructor === soydata.SanitizedHtml);
1678    return value.content;
1679  }
1680  return soy.$$stripHtmlTags(value, soy.esc.$$SAFE_TAG_WHITELIST_);
1681};
1682
1683
1684/**
1685 * Escapes HTML special characters in a string so that it can be embedded in
1686 * RCDATA.
1687 * <p>
1688 * Escapes HTML special characters so that the value will not prematurely end
1689 * the body of a tag like {@code <textarea>} or {@code <title>}. RCDATA tags
1690 * cannot contain other HTML entities, so it is not strictly necessary to escape
1691 * HTML special characters except when part of that text looks like an HTML
1692 * entity or like a close tag : {@code </textarea>}.
1693 * <p>
1694 * Will normalize known safe HTML to make sure that sanitized HTML (which could
1695 * contain an innocuous {@code </textarea>} don't prematurely end an RCDATA
1696 * element.
1697 *
1698 * @param {*} value The string-like value to be escaped. May not be a string,
1699 *     but the value will be coerced to a string.
1700 * @return {string} An escaped version of value.
1701 */
1702soy.$$escapeHtmlRcdata = function(value) {
1703  if (value && value.contentKind &&
1704      value.contentKind === goog.soy.data.SanitizedContentKind.HTML) {
1705    goog.asserts.assert(
1706        value.constructor === soydata.SanitizedHtml);
1707    return soy.esc.$$normalizeHtmlHelper(value.content);
1708  }
1709  return soy.esc.$$escapeHtmlHelper(value);
1710};
1711
1712
1713/**
1714 * Matches any/only HTML5 void elements' start tags.
1715 * See http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
1716 * @type {RegExp}
1717 * @private
1718 */
1719soy.$$HTML5_VOID_ELEMENTS_ = new RegExp(
1720    '^<(?:area|base|br|col|command|embed|hr|img|input' +
1721    '|keygen|link|meta|param|source|track|wbr)\\b');
1722
1723
1724/**
1725 * Removes HTML tags from a string of known safe HTML.
1726 * If opt_tagWhitelist is not specified or is empty, then
1727 * the result can be used as an attribute value.
1728 *
1729 * @param {*} value The HTML to be escaped. May not be a string, but the
1730 *     value will be coerced to a string.
1731 * @param {Object.<string, number>=} opt_tagWhitelist Has an own property whose
1732 *     name is a lower-case tag name and whose value is {@code 1} for
1733 *     each element that is allowed in the output.
1734 * @return {string} A representation of value without disallowed tags,
1735 *     HTML comments, or other non-text content.
1736 */
1737soy.$$stripHtmlTags = function(value, opt_tagWhitelist) {
1738  if (!opt_tagWhitelist) {
1739    // If we have no white-list, then use a fast track which elides all tags.
1740    return String(value).replace(soy.esc.$$HTML_TAG_REGEX_, '')
1741        // This is just paranoia since callers should normalize the result
1742        // anyway, but if they didn't, it would be necessary to ensure that
1743        // after the first replace non-tag uses of < do not recombine into
1744        // tags as in "<<foo>script>alert(1337)</<foo>script>".
1745        .replace(soy.esc.$$LT_REGEX_, '&lt;');
1746  }
1747
1748  // Escapes '[' so that we can use [123] below to mark places where tags
1749  // have been removed.
1750  var html = String(value).replace(/\[/g, '&#91;');
1751
1752  // Consider all uses of '<' and replace whitelisted tags with markers like
1753  // [1] which are indices into a list of approved tag names.
1754  // Replace all other uses of < and > with entities.
1755  var tags = [];
1756  html = html.replace(
1757    soy.esc.$$HTML_TAG_REGEX_,
1758    function(tok, tagName) {
1759      if (tagName) {
1760        tagName = tagName.toLowerCase();
1761        if (opt_tagWhitelist.hasOwnProperty(tagName) &&
1762            opt_tagWhitelist[tagName]) {
1763          var start = tok.charAt(1) === '/' ? '</' : '<';
1764          var index = tags.length;
1765          tags[index] = start + tagName + '>';
1766          return '[' + index + ']';
1767        }
1768      }
1769      return '';
1770    });
1771
1772  // Escape HTML special characters. Now there are no '<' in html that could
1773  // start a tag.
1774  html = soy.esc.$$normalizeHtmlHelper(html);
1775
1776  var finalCloseTags = soy.$$balanceTags_(tags);
1777
1778  // Now html contains no tags or less-than characters that could become
1779  // part of a tag via a replacement operation and tags only contains
1780  // approved tags.
1781  // Reinsert the white-listed tags.
1782  html = html.replace(
1783       /\[(\d+)\]/g, function(_, index) { return tags[index]; });
1784
1785  // Close any still open tags.
1786  // This prevents unclosed formatting elements like <ol> and <table> from
1787  // breaking the layout of containing HTML.
1788  return html + finalCloseTags;
1789};
1790
1791
1792/**
1793 * Throw out any close tags that don't correspond to start tags.
1794 * If {@code <table>} is used for formatting, embedded HTML shouldn't be able
1795 * to use a mismatched {@code </table>} to break page layout.
1796 *
1797 * @param {Array.<string>} tags an array of tags that will be modified in place
1798 *    include tags, the empty string, or concatenations of empty tags.
1799 * @return {string} zero or more closed tags that close all elements that are
1800 *    opened in tags but not closed.
1801 * @private
1802 */
1803soy.$$balanceTags_ = function(tags) {
1804  var open = [];
1805  for (var i = 0, n = tags.length; i < n; ++i) {
1806    var tag = tags[i];
1807    if (tag.charAt(1) === '/') {
1808      var openTagIndex = open.length - 1;
1809      // NOTE: This is essentially lastIndexOf, but it's not supported in IE.
1810      while (openTagIndex >= 0 && open[openTagIndex] != tag) {
1811        openTagIndex--;
1812      }
1813      if (openTagIndex < 0) {
1814        tags[i] = '';  // Drop close tag.
1815      } else {
1816        tags[i] = open.slice(openTagIndex).reverse().join('');
1817        open.length = openTagIndex;
1818      }
1819    } else if (!soy.$$HTML5_VOID_ELEMENTS_.test(tag)) {
1820      open.push('</' + tag.substring(1));
1821    }
1822  }
1823  return open.reverse().join('');
1824};
1825
1826
1827/**
1828 * Escapes HTML special characters in an HTML attribute value.
1829 *
1830 * @param {*} value The HTML to be escaped. May not be a string, but the
1831 *     value will be coerced to a string.
1832 * @return {string} An escaped version of value.
1833 */
1834soy.$$escapeHtmlAttribute = function(value) {
1835  if (value && value.contentKind) {
1836    // NOTE: We don't accept ATTRIBUTES here because ATTRIBUTES is
1837    // actually not the attribute value context, but instead k/v pairs.
1838    if (value.contentKind === goog.soy.data.SanitizedContentKind.HTML) {
1839      // NOTE: After removing tags, we also escape quotes ("normalize") so that
1840      // the HTML can be embedded in attribute context.
1841      goog.asserts.assert(
1842          value.constructor === soydata.SanitizedHtml);
1843      return soy.esc.$$normalizeHtmlHelper(soy.$$stripHtmlTags(value.content));
1844    }
1845  }
1846  return soy.esc.$$escapeHtmlHelper(value);
1847};
1848
1849
1850/**
1851 * Escapes HTML special characters in a string including space and other
1852 * characters that can end an unquoted HTML attribute value.
1853 *
1854 * @param {*} value The HTML to be escaped. May not be a string, but the
1855 *     value will be coerced to a string.
1856 * @return {string} An escaped version of value.
1857 */
1858soy.$$escapeHtmlAttributeNospace = function(value) {
1859  if (value && value.contentKind) {
1860    if (value.contentKind === goog.soy.data.SanitizedContentKind.HTML) {
1861      goog.asserts.assert(value.constructor ===
1862          soydata.SanitizedHtml);
1863      return soy.esc.$$normalizeHtmlNospaceHelper(
1864          soy.$$stripHtmlTags(value.content));
1865    }
1866  }
1867  return soy.esc.$$escapeHtmlNospaceHelper(value);
1868};
1869
1870
1871/**
1872 * Filters out strings that cannot be a substring of a valid HTML attribute.
1873 *
1874 * Note the input is expected to be key=value pairs.
1875 *
1876 * @param {*} value The value to escape. May not be a string, but the value
1877 *     will be coerced to a string.
1878 * @return {string} A valid HTML attribute name part or name/value pair.
1879 *     {@code "zSoyz"} if the input is invalid.
1880 */
1881soy.$$filterHtmlAttributes = function(value) {
1882  // NOTE: Explicitly no support for SanitizedContentKind.HTML, since that is
1883  // meaningless in this context, which is generally *between* html attributes.
1884  if (value &&
1885      value.contentKind === goog.soy.data.SanitizedContentKind.ATTRIBUTES) {
1886    goog.asserts.assert(value.constructor ===
1887        soydata.SanitizedHtmlAttribute);
1888    // Add a space at the end to ensure this won't get merged into following
1889    // attributes, unless the interpretation is unambiguous (ending with quotes
1890    // or a space).
1891    return value.content.replace(/([^"'\s])$/, '$1 ');
1892  }
1893  // TODO: Dynamically inserting attributes that aren't marked as trusted is
1894  // probably unnecessary.  Any filtering done here will either be inadequate
1895  // for security or not flexible enough.  Having clients use kind="attributes"
1896  // in parameters seems like a wiser idea.
1897  return soy.esc.$$filterHtmlAttributesHelper(value);
1898};
1899
1900
1901/**
1902 * Filters out strings that cannot be a substring of a valid HTML element name.
1903 *
1904 * @param {*} value The value to escape. May not be a string, but the value
1905 *     will be coerced to a string.
1906 * @return {string} A valid HTML element name part.
1907 *     {@code "zSoyz"} if the input is invalid.
1908 */
1909soy.$$filterHtmlElementName = function(value) {
1910  // NOTE: We don't accept any SanitizedContent here. HTML indicates valid
1911  // PCDATA, not tag names. A sloppy developer shouldn't be able to cause an
1912  // exploit:
1913  // ... {let userInput}script src=http://evil.com/evil.js{/let} ...
1914  // ... {param tagName kind="html"}{$userInput}{/param} ...
1915  // ... <{$tagName}>Hello World</{$tagName}>
1916  return soy.esc.$$filterHtmlElementNameHelper(value);
1917};
1918
1919
1920/**
1921 * Escapes characters in the value to make it valid content for a JS string
1922 * literal.
1923 *
1924 * @param {*} value The value to escape. May not be a string, but the value
1925 *     will be coerced to a string.
1926 * @return {string} An escaped version of value.
1927 * @deprecated
1928 */
1929soy.$$escapeJs = function(value) {
1930  return soy.$$escapeJsString(value);
1931};
1932
1933
1934/**
1935 * Escapes characters in the value to make it valid content for a JS string
1936 * literal.
1937 *
1938 * @param {*} value The value to escape. May not be a string, but the value
1939 *     will be coerced to a string.
1940 * @return {string} An escaped version of value.
1941 */
1942soy.$$escapeJsString = function(value) {
1943  if (value &&
1944      value.contentKind === goog.soy.data.SanitizedContentKind.JS_STR_CHARS) {
1945    // TODO: It might still be worthwhile to normalize it to remove
1946    // unescaped quotes, null, etc: replace(/(?:^|[^\])['"]/g, '\\$
1947    goog.asserts.assert(value.constructor ===
1948        soydata.SanitizedJsStrChars);
1949    return value.content;
1950  }
1951  return soy.esc.$$escapeJsStringHelper(value);
1952};
1953
1954
1955/**
1956 * Encodes a value as a JavaScript literal.
1957 *
1958 * @param {*} value The value to escape. May not be a string, but the value
1959 *     will be coerced to a string.
1960 * @return {string} A JavaScript code representation of the input.
1961 */
1962soy.$$escapeJsValue = function(value) {
1963  // We surround values with spaces so that they can't be interpolated into
1964  // identifiers by accident.
1965  // We could use parentheses but those might be interpreted as a function call.
1966  if (value == null) {  // Intentionally matches undefined.
1967    // Java returns null from maps where there is no corresponding key while
1968    // JS returns undefined.
1969    // We always output null for compatibility with Java which does not have a
1970    // distinct undefined value.
1971    return ' null ';
1972  }
1973  if (value.contentKind == goog.soy.data.SanitizedContentKind.JS) {
1974    goog.asserts.assert(value.constructor ===
1975        soydata.SanitizedJs);
1976    return value.content;
1977  }
1978  switch (typeof value) {
1979    case 'boolean': case 'number':
1980      return ' ' + value + ' ';
1981    default:
1982      return "'" + soy.esc.$$escapeJsStringHelper(String(value)) + "'";
1983  }
1984};
1985
1986
1987/**
1988 * Escapes characters in the string to make it valid content for a JS regular
1989 * expression literal.
1990 *
1991 * @param {*} value The value to escape. May not be a string, but the value
1992 *     will be coerced to a string.
1993 * @return {string} An escaped version of value.
1994 */
1995soy.$$escapeJsRegex = function(value) {
1996  return soy.esc.$$escapeJsRegexHelper(value);
1997};
1998
1999
2000/**
2001 * Matches all URI mark characters that conflict with HTML attribute delimiters
2002 * or that cannot appear in a CSS uri.
2003 * From <a href="http://www.w3.org/TR/CSS2/grammar.html">G.2: CSS grammar</a>
2004 * <pre>
2005 *     url        ([!#$%&*-~]|{nonascii}|{escape})*
2006 * </pre>
2007 *
2008 * @type {RegExp}
2009 * @private
2010 */
2011soy.$$problematicUriMarks_ = /['()]/g;
2012
2013/**
2014 * @param {string} ch A single character in {@link soy.$$problematicUriMarks_}.
2015 * @return {string}
2016 * @private
2017 */
2018soy.$$pctEncode_ = function(ch) {
2019  return '%' + ch.charCodeAt(0).toString(16);
2020};
2021
2022/**
2023 * Escapes a string so that it can be safely included in a URI.
2024 *
2025 * @param {*} value The value to escape. May not be a string, but the value
2026 *     will be coerced to a string.
2027 * @return {string} An escaped version of value.
2028 */
2029soy.$$escapeUri = function(value) {
2030  if (value && value.contentKind === goog.soy.data.SanitizedContentKind.URI) {
2031    goog.asserts.assert(value.constructor ===
2032        soydata.SanitizedUri);
2033    return soy.$$normalizeUri(value);
2034  }
2035  // Apostophes and parentheses are not matched by encodeURIComponent.
2036  // They are technically special in URIs, but only appear in the obsolete mark
2037  // production in Appendix D.2 of RFC 3986, so can be encoded without changing
2038  // semantics.
2039  var encoded = soy.esc.$$escapeUriHelper(value);
2040  soy.$$problematicUriMarks_.lastIndex = 0;
2041  if (soy.$$problematicUriMarks_.test(encoded)) {
2042    return encoded.replace(soy.$$problematicUriMarks_, soy.$$pctEncode_);
2043  }
2044  return encoded;
2045};
2046
2047
2048/**
2049 * Removes rough edges from a URI by escaping any raw HTML/JS string delimiters.
2050 *
2051 * @param {*} value The value to escape. May not be a string, but the value
2052 *     will be coerced to a string.
2053 * @return {string} An escaped version of value.
2054 */
2055soy.$$normalizeUri = function(value) {
2056  return soy.esc.$$normalizeUriHelper(value);
2057};
2058
2059
2060/**
2061 * Vets a URI's protocol and removes rough edges from a URI by escaping
2062 * any raw HTML/JS string delimiters.
2063 *
2064 * @param {*} value The value to escape. May not be a string, but the value
2065 *     will be coerced to a string.
2066 * @return {string} An escaped version of value.
2067 */
2068soy.$$filterNormalizeUri = function(value) {
2069  if (value && value.contentKind == goog.soy.data.SanitizedContentKind.URI) {
2070    goog.asserts.assert(value.constructor ===
2071        soydata.SanitizedUri);
2072    return soy.$$normalizeUri(value);
2073  }
2074  return soy.esc.$$filterNormalizeUriHelper(value);
2075};
2076
2077
2078/**
2079 * Escapes a string so it can safely be included inside a quoted CSS string.
2080 *
2081 * @param {*} value The value to escape. May not be a string, but the value
2082 *     will be coerced to a string.
2083 * @return {string} An escaped version of value.
2084 */
2085soy.$$escapeCssString = function(value) {
2086  return soy.esc.$$escapeCssStringHelper(value);
2087};
2088
2089
2090/**
2091 * Encodes a value as a CSS identifier part, keyword, or quantity.
2092 *
2093 * @param {*} value The value to escape. May not be a string, but the value
2094 *     will be coerced to a string.
2095 * @return {string} A safe CSS identifier part, keyword, or quanitity.
2096 */
2097soy.$$filterCssValue = function(value) {
2098  if (value && value.contentKind === goog.soy.data.SanitizedContentKind.CSS) {
2099    goog.asserts.assert(value.constructor ===
2100        soydata.SanitizedCss);
2101    return value.content;
2102  }
2103  // Uses == to intentionally match null and undefined for Java compatibility.
2104  if (value == null) {
2105    return '';
2106  }
2107  return soy.esc.$$filterCssValueHelper(value);
2108};
2109
2110
2111/**
2112 * Sanity-checks noAutoescape input for explicitly tainted content.
2113 *
2114 * SanitizedContentKind.TEXT is used to explicitly mark input that was never
2115 * meant to be used unescaped.
2116 *
2117 * @param {*} value The value to filter.
2118 * @return {string} The value, that we dearly hope will not cause an attack.
2119 */
2120soy.$$filterNoAutoescape = function(value) {
2121  if (value && value.contentKind === goog.soy.data.SanitizedContentKind.TEXT) {
2122    // Fail in development mode.
2123    goog.asserts.fail(
2124        'Tainted SanitizedContentKind.TEXT for |noAutoescape: `%s`',
2125        [value.content]);
2126    // Return innocuous data in production.
2127    return 'zSoyz';
2128  }
2129  return String(value);
2130};
2131
2132
2133// -----------------------------------------------------------------------------
2134// Basic directives/functions.
2135
2136
2137/**
2138 * Converts \r\n, \r, and \n to <br>s
2139 * @param {*} str The string in which to convert newlines.
2140 * @return {string} A copy of {@code str} with converted newlines.
2141 */
2142soy.$$changeNewlineToBr = function(str) {
2143  return goog.string.newLineToBr(String(str), false);
2144};
2145
2146
2147/**
2148 * Inserts word breaks ('wbr' tags) into a HTML string at a given interval. The
2149 * counter is reset if a space is encountered. Word breaks aren't inserted into
2150 * HTML tags or entities. Entites count towards the character count; HTML tags
2151 * do not.
2152 *
2153 * @param {*} str The HTML string to insert word breaks into. Can be other
2154 *     types, but the value will be coerced to a string.
2155 * @param {number} maxCharsBetweenWordBreaks Maximum number of non-space
2156 *     characters to allow before adding a word break.
2157 * @return {string} The string including word breaks.
2158 */
2159soy.$$insertWordBreaks = function(str, maxCharsBetweenWordBreaks) {
2160  return goog.format.insertWordBreaks(String(str), maxCharsBetweenWordBreaks);
2161};
2162
2163
2164/**
2165 * Truncates a string to a given max length (if it's currently longer),
2166 * optionally adding ellipsis at the end.
2167 *
2168 * @param {*} str The string to truncate. Can be other types, but the value will
2169 *     be coerced to a string.
2170 * @param {number} maxLen The maximum length of the string after truncation
2171 *     (including ellipsis, if applicable).
2172 * @param {boolean} doAddEllipsis Whether to add ellipsis if the string needs
2173 *     truncation.
2174 * @return {string} The string after truncation.
2175 */
2176soy.$$truncate = function(str, maxLen, doAddEllipsis) {
2177
2178  str = String(str);
2179  if (str.length <= maxLen) {
2180    return str;  // no need to truncate
2181  }
2182
2183  // If doAddEllipsis, either reduce maxLen to compensate, or else if maxLen is
2184  // too small, just turn off doAddEllipsis.
2185  if (doAddEllipsis) {
2186    if (maxLen > 3) {
2187      maxLen -= 3;
2188    } else {
2189      doAddEllipsis = false;
2190    }
2191  }
2192
2193  // Make sure truncating at maxLen doesn't cut up a unicode surrogate pair.
2194  if (soy.$$isHighSurrogate_(str.charAt(maxLen - 1)) &&
2195      soy.$$isLowSurrogate_(str.charAt(maxLen))) {
2196    maxLen -= 1;
2197  }
2198
2199  // Truncate.
2200  str = str.substring(0, maxLen);
2201
2202  // Add ellipsis.
2203  if (doAddEllipsis) {
2204    str += '...';
2205  }
2206
2207  return str;
2208};
2209
2210/**
2211 * Private helper for $$truncate() to check whether a char is a high surrogate.
2212 * @param {string} ch The char to check.
2213 * @return {boolean} Whether the given char is a unicode high surrogate.
2214 * @private
2215 */
2216soy.$$isHighSurrogate_ = function(ch) {
2217  return 0xD800 <= ch && ch <= 0xDBFF;
2218};
2219
2220/**
2221 * Private helper for $$truncate() to check whether a char is a low surrogate.
2222 * @param {string} ch The char to check.
2223 * @return {boolean} Whether the given char is a unicode low surrogate.
2224 * @private
2225 */
2226soy.$$isLowSurrogate_ = function(ch) {
2227  return 0xDC00 <= ch && ch <= 0xDFFF;
2228};
2229
2230
2231// -----------------------------------------------------------------------------
2232// Bidi directives/functions.
2233
2234
2235/**
2236 * Cache of bidi formatter by context directionality, so we don't keep on
2237 * creating new objects.
2238 * @type {!Object.<!goog.i18n.BidiFormatter>}
2239 * @private
2240 */
2241soy.$$bidiFormatterCache_ = {};
2242
2243
2244/**
2245 * Returns cached bidi formatter for bidiGlobalDir, or creates a new one.
2246 * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
2247 *     if rtl, 0 if unknown.
2248 * @return {goog.i18n.BidiFormatter} A formatter for bidiGlobalDir.
2249 * @private
2250 */
2251soy.$$getBidiFormatterInstance_ = function(bidiGlobalDir) {
2252  return soy.$$bidiFormatterCache_[bidiGlobalDir] ||
2253         (soy.$$bidiFormatterCache_[bidiGlobalDir] =
2254             new goog.i18n.BidiFormatter(bidiGlobalDir));
2255};
2256
2257
2258/**
2259 * Estimate the overall directionality of text. If opt_isHtml, makes sure to
2260 * ignore the LTR nature of the mark-up and escapes in text, making the logic
2261 * suitable for HTML and HTML-escaped text.
2262 * @param {string} text The text whose directionality is to be estimated.
2263 * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
2264 *     Default: false.
2265 * @return {number} 1 if text is LTR, -1 if it is RTL, and 0 if it is neutral.
2266 */
2267soy.$$bidiTextDir = function(text, opt_isHtml) {
2268  if (!text) {
2269    return 0;
2270  }
2271  return goog.i18n.bidi.detectRtlDirectionality(text, opt_isHtml) ? -1 : 1;
2272};
2273
2274
2275/**
2276 * Returns 'dir="ltr"' or 'dir="rtl"', depending on text's estimated
2277 * directionality, if it is not the same as bidiGlobalDir.
2278 * Otherwise, returns the empty string.
2279 * If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes
2280 * in text, making the logic suitable for HTML and HTML-escaped text.
2281 * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
2282 *     if rtl, 0 if unknown.
2283 * @param {string} text The text whose directionality is to be estimated.
2284 * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
2285 *     Default: false.
2286 * @return {soydata.SanitizedHtmlAttribute} 'dir="rtl"' for RTL text in non-RTL
2287 *     context; 'dir="ltr"' for LTR text in non-LTR context;
2288 *     else, the empty string.
2289 */
2290soy.$$bidiDirAttr = function(bidiGlobalDir, text, opt_isHtml) {
2291  return soydata.VERY_UNSAFE.ordainSanitizedHtmlAttribute(
2292      soy.$$getBidiFormatterInstance_(bidiGlobalDir).dirAttr(text, opt_isHtml));
2293};
2294
2295
2296/**
2297 * Returns a Unicode BiDi mark matching bidiGlobalDir (LRM or RLM) if the
2298 * directionality or the exit directionality of text are opposite to
2299 * bidiGlobalDir. Otherwise returns the empty string.
2300 * If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes
2301 * in text, making the logic suitable for HTML and HTML-escaped text.
2302 * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
2303 *     if rtl, 0 if unknown.
2304 * @param {string} text The text whose directionality is to be estimated.
2305 * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
2306 *     Default: false.
2307 * @return {string} A Unicode bidi mark matching bidiGlobalDir, or the empty
2308 *     string when text's overall and exit directionalities both match
2309 *     bidiGlobalDir, or bidiGlobalDir is 0 (unknown).
2310 */
2311soy.$$bidiMarkAfter = function(bidiGlobalDir, text, opt_isHtml) {
2312  var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
2313  return formatter.markAfter(text, opt_isHtml);
2314};
2315
2316
2317/**
2318 * Returns str wrapped in a <span dir="ltr|rtl"> according to its directionality
2319 * - but only if that is neither neutral nor the same as the global context.
2320 * Otherwise, returns str unchanged.
2321 * Always treats str as HTML/HTML-escaped, i.e. ignores mark-up and escapes when
2322 * estimating str's directionality.
2323 * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
2324 *     if rtl, 0 if unknown.
2325 * @param {*} str The string to be wrapped. Can be other types, but the value
2326 *     will be coerced to a string.
2327 * @return {string} The wrapped string.
2328 */
2329soy.$$bidiSpanWrap = function(bidiGlobalDir, str) {
2330  var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
2331  return formatter.spanWrap(str + '', true);
2332};
2333
2334
2335/**
2336 * Returns str wrapped in Unicode BiDi formatting characters according to its
2337 * directionality, i.e. either LRE or RLE at the beginning and PDF at the end -
2338 * but only if str's directionality is neither neutral nor the same as the
2339 * global context. Otherwise, returns str unchanged.
2340 * Always treats str as HTML/HTML-escaped, i.e. ignores mark-up and escapes when
2341 * estimating str's directionality.
2342 * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
2343 *     if rtl, 0 if unknown.
2344 * @param {*} str The string to be wrapped. Can be other types, but the value
2345 *     will be coerced to a string.
2346 * @return {string} The wrapped string.
2347 */
2348soy.$$bidiUnicodeWrap = function(bidiGlobalDir, str) {
2349  var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
2350  return formatter.unicodeWrap(str + '', true);
2351};
2352
2353
2354// -----------------------------------------------------------------------------
2355// Generated code.
2356
2357
2358// START GENERATED CODE FOR ESCAPERS.
2359
2360/**
2361 * @type {function (*) : string}
2362 */
2363soy.esc.$$escapeUriHelper = function(v) {
2364  return goog.string.urlEncode(String(v));
2365};
2366
2367/**
2368 * Maps charcters to the escaped versions for the named escape directives.
2369 * @type {Object.<string, string>}
2370 * @private
2371 */
2372soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_ = {
2373  '\x00': '\x26#0;',
2374  '\x22': '\x26quot;',
2375  '\x26': '\x26amp;',
2376  '\x27': '\x26#39;',
2377  '\x3c': '\x26lt;',
2378  '\x3e': '\x26gt;',
2379  '\x09': '\x26#9;',
2380  '\x0a': '\x26#10;',
2381  '\x0b': '\x26#11;',
2382  '\x0c': '\x26#12;',
2383  '\x0d': '\x26#13;',
2384  ' ': '\x26#32;',
2385  '-': '\x26#45;',
2386  '\/': '\x26#47;',
2387  '\x3d': '\x26#61;',
2388  '`': '\x26#96;',
2389  '\x85': '\x26#133;',
2390  '\xa0': '\x26#160;',
2391  '\u2028': '\x26#8232;',
2392  '\u2029': '\x26#8233;'
2393};
2394
2395/**
2396 * A function that can be used with String.replace..
2397 * @param {string} ch A single character matched by a compatible matcher.
2398 * @return {string} A token in the output language.
2399 * @private
2400 */
2401soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_ = function(ch) {
2402  return soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_[ch];
2403};
2404
2405/**
2406 * Maps charcters to the escaped versions for the named escape directives.
2407 * @type {Object.<string, string>}
2408 * @private
2409 */
2410soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = {
2411  '\x00': '\\x00',
2412  '\x08': '\\x08',
2413  '\x09': '\\t',
2414  '\x0a': '\\n',
2415  '\x0b': '\\x0b',
2416  '\x0c': '\\f',
2417  '\x0d': '\\r',
2418  '\x22': '\\x22',
2419  '\x26': '\\x26',
2420  '\x27': '\\x27',
2421  '\/': '\\\/',
2422  '\x3c': '\\x3c',
2423  '\x3d': '\\x3d',
2424  '\x3e': '\\x3e',
2425  '\\': '\\\\',
2426  '\x85': '\\x85',
2427  '\u2028': '\\u2028',
2428  '\u2029': '\\u2029',
2429  '$': '\\x24',
2430  '(': '\\x28',
2431  ')': '\\x29',
2432  '*': '\\x2a',
2433  '+': '\\x2b',
2434  ',': '\\x2c',
2435  '-': '\\x2d',
2436  '.': '\\x2e',
2437  ':': '\\x3a',
2438  '?': '\\x3f',
2439  '[': '\\x5b',
2440  ']': '\\x5d',
2441  '^': '\\x5e',
2442  '{': '\\x7b',
2443  '|': '\\x7c',
2444  '}': '\\x7d'
2445};
2446
2447/**
2448 * A function that can be used with String.replace..
2449 * @param {string} ch A single character matched by a compatible matcher.
2450 * @return {string} A token in the output language.
2451 * @private
2452 */
2453soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = function(ch) {
2454  return soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_[ch];
2455};
2456
2457/**
2458 * Maps charcters to the escaped versions for the named escape directives.
2459 * @type {Object.<string, string>}
2460 * @private
2461 */
2462soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_CSS_STRING_ = {
2463  '\x00': '\\0 ',
2464  '\x08': '\\8 ',
2465  '\x09': '\\9 ',
2466  '\x0a': '\\a ',
2467  '\x0b': '\\b ',
2468  '\x0c': '\\c ',
2469  '\x0d': '\\d ',
2470  '\x22': '\\22 ',
2471  '\x26': '\\26 ',
2472  '\x27': '\\27 ',
2473  '(': '\\28 ',
2474  ')': '\\29 ',
2475  '*': '\\2a ',
2476  '\/': '\\2f ',
2477  ':': '\\3a ',
2478  ';': '\\3b ',
2479  '\x3c': '\\3c ',
2480  '\x3d': '\\3d ',
2481  '\x3e': '\\3e ',
2482  '@': '\\40 ',
2483  '\\': '\\5c ',
2484  '{': '\\7b ',
2485  '}': '\\7d ',
2486  '\x85': '\\85 ',
2487  '\xa0': '\\a0 ',
2488  '\u2028': '\\2028 ',
2489  '\u2029': '\\2029 '
2490};
2491
2492/**
2493 * A function that can be used with String.replace..
2494 * @param {string} ch A single character matched by a compatible matcher.
2495 * @return {string} A token in the output language.
2496 * @private
2497 */
2498soy.esc.$$REPLACER_FOR_ESCAPE_CSS_STRING_ = function(ch) {
2499  return soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_CSS_STRING_[ch];
2500};
2501
2502/**
2503 * Maps charcters to the escaped versions for the named escape directives.
2504 * @type {Object.<string, string>}
2505 * @private
2506 */
2507soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_ = {
2508  '\x00': '%00',
2509  '\x01': '%01',
2510  '\x02': '%02',
2511  '\x03': '%03',
2512  '\x04': '%04',
2513  '\x05': '%05',
2514  '\x06': '%06',
2515  '\x07': '%07',
2516  '\x08': '%08',
2517  '\x09': '%09',
2518  '\x0a': '%0A',
2519  '\x0b': '%0B',
2520  '\x0c': '%0C',
2521  '\x0d': '%0D',
2522  '\x0e': '%0E',
2523  '\x0f': '%0F',
2524  '\x10': '%10',
2525  '\x11': '%11',
2526  '\x12': '%12',
2527  '\x13': '%13',
2528  '\x14': '%14',
2529  '\x15': '%15',
2530  '\x16': '%16',
2531  '\x17': '%17',
2532  '\x18': '%18',
2533  '\x19': '%19',
2534  '\x1a': '%1A',
2535  '\x1b': '%1B',
2536  '\x1c': '%1C',
2537  '\x1d': '%1D',
2538  '\x1e': '%1E',
2539  '\x1f': '%1F',
2540  ' ': '%20',
2541  '\x22': '%22',
2542  '\x27': '%27',
2543  '(': '%28',
2544  ')': '%29',
2545  '\x3c': '%3C',
2546  '\x3e': '%3E',
2547  '\\': '%5C',
2548  '{': '%7B',
2549  '}': '%7D',
2550  '\x7f': '%7F',
2551  '\x85': '%C2%85',
2552  '\xa0': '%C2%A0',
2553  '\u2028': '%E2%80%A8',
2554  '\u2029': '%E2%80%A9',
2555  '\uff01': '%EF%BC%81',
2556  '\uff03': '%EF%BC%83',
2557  '\uff04': '%EF%BC%84',
2558  '\uff06': '%EF%BC%86',
2559  '\uff07': '%EF%BC%87',
2560  '\uff08': '%EF%BC%88',
2561  '\uff09': '%EF%BC%89',
2562  '\uff0a': '%EF%BC%8A',
2563  '\uff0b': '%EF%BC%8B',
2564  '\uff0c': '%EF%BC%8C',
2565  '\uff0f': '%EF%BC%8F',
2566  '\uff1a': '%EF%BC%9A',
2567  '\uff1b': '%EF%BC%9B',
2568  '\uff1d': '%EF%BC%9D',
2569  '\uff1f': '%EF%BC%9F',
2570  '\uff20': '%EF%BC%A0',
2571  '\uff3b': '%EF%BC%BB',
2572  '\uff3d': '%EF%BC%BD'
2573};
2574
2575/**
2576 * A function that can be used with String.replace..
2577 * @param {string} ch A single character matched by a compatible matcher.
2578 * @return {string} A token in the output language.
2579 * @private
2580 */
2581soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_ = function(ch) {
2582  return soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_[ch];
2583};
2584
2585/**
2586 * Matches characters that need to be escaped for the named directives.
2587 * @type RegExp
2588 * @private
2589 */
2590soy.esc.$$MATCHER_FOR_ESCAPE_HTML_ = /[\x00\x22\x26\x27\x3c\x3e]/g;
2591
2592/**
2593 * Matches characters that need to be escaped for the named directives.
2594 * @type RegExp
2595 * @private
2596 */
2597soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_ = /[\x00\x22\x27\x3c\x3e]/g;
2598
2599/**
2600 * Matches characters that need to be escaped for the named directives.
2601 * @type RegExp
2602 * @private
2603 */
2604soy.esc.$$MATCHER_FOR_ESCAPE_HTML_NOSPACE_ = /[\x00\x09-\x0d \x22\x26\x27\x2d\/\x3c-\x3e`\x85\xa0\u2028\u2029]/g;
2605
2606/**
2607 * Matches characters that need to be escaped for the named directives.
2608 * @type RegExp
2609 * @private
2610 */
2611soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_NOSPACE_ = /[\x00\x09-\x0d \x22\x27\x2d\/\x3c-\x3e`\x85\xa0\u2028\u2029]/g;
2612
2613/**
2614 * Matches characters that need to be escaped for the named directives.
2615 * @type RegExp
2616 * @private
2617 */
2618soy.esc.$$MATCHER_FOR_ESCAPE_JS_STRING_ = /[\x00\x08-\x0d\x22\x26\x27\/\x3c-\x3e\\\x85\u2028\u2029]/g;
2619
2620/**
2621 * Matches characters that need to be escaped for the named directives.
2622 * @type RegExp
2623 * @private
2624 */
2625soy.esc.$$MATCHER_FOR_ESCAPE_JS_REGEX_ = /[\x00\x08-\x0d\x22\x24\x26-\/\x3a\x3c-\x3f\x5b-\x5e\x7b-\x7d\x85\u2028\u2029]/g;
2626
2627/**
2628 * Matches characters that need to be escaped for the named directives.
2629 * @type RegExp
2630 * @private
2631 */
2632soy.esc.$$MATCHER_FOR_ESCAPE_CSS_STRING_ = /[\x00\x08-\x0d\x22\x26-\x2a\/\x3a-\x3e@\\\x7b\x7d\x85\xa0\u2028\u2029]/g;
2633
2634/**
2635 * Matches characters that need to be escaped for the named directives.
2636 * @type RegExp
2637 * @private
2638 */
2639soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_ = /[\x00- \x22\x27-\x29\x3c\x3e\\\x7b\x7d\x7f\x85\xa0\u2028\u2029\uff01\uff03\uff04\uff06-\uff0c\uff0f\uff1a\uff1b\uff1d\uff1f\uff20\uff3b\uff3d]/g;
2640
2641/**
2642 * A pattern that vets values produced by the named directives.
2643 * @type RegExp
2644 * @private
2645 */
2646soy.esc.$$FILTER_FOR_FILTER_CSS_VALUE_ = /^(?!-*(?:expression|(?:moz-)?binding))(?:[.#]?-?(?:[_a-z0-9-]+)(?:-[_a-z0-9-]+)*-?|-?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[a-z]{1,2}|%)?|!important|)$/i;
2647
2648/**
2649 * A pattern that vets values produced by the named directives.
2650 * @type RegExp
2651 * @private
2652 */
2653soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_ = /^(?:(?:https?|mailto):|[^&:\/?#]*(?:[\/?#]|$))/i;
2654
2655/**
2656 * A pattern that vets values produced by the named directives.
2657 * @type RegExp
2658 * @private
2659 */
2660soy.esc.$$FILTER_FOR_FILTER_HTML_ATTRIBUTES_ = /^(?!style|on|action|archive|background|cite|classid|codebase|data|dsync|href|longdesc|src|usemap)(?:[a-z0-9_$:-]*)$/i;
2661
2662/**
2663 * A pattern that vets values produced by the named directives.
2664 * @type RegExp
2665 * @private
2666 */
2667soy.esc.$$FILTER_FOR_FILTER_HTML_ELEMENT_NAME_ = /^(?!script|style|title|textarea|xmp|no)[a-z0-9_$:-]*$/i;
2668
2669/**
2670 * A helper for the Soy directive |escapeHtml
2671 * @param {*} value Can be of any type but will be coerced to a string.
2672 * @return {string} The escaped text.
2673 */
2674soy.esc.$$escapeHtmlHelper = function(value) {
2675  var str = String(value);
2676  return str.replace(
2677      soy.esc.$$MATCHER_FOR_ESCAPE_HTML_,
2678      soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_);
2679};
2680
2681/**
2682 * A helper for the Soy directive |normalizeHtml
2683 * @param {*} value Can be of any type but will be coerced to a string.
2684 * @return {string} The escaped text.
2685 */
2686soy.esc.$$normalizeHtmlHelper = function(value) {
2687  var str = String(value);
2688  return str.replace(
2689      soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_,
2690      soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_);
2691};
2692
2693/**
2694 * A helper for the Soy directive |escapeHtmlNospace
2695 * @param {*} value Can be of any type but will be coerced to a string.
2696 * @return {string} The escaped text.
2697 */
2698soy.esc.$$escapeHtmlNospaceHelper = function(value) {
2699  var str = String(value);
2700  return str.replace(
2701      soy.esc.$$MATCHER_FOR_ESCAPE_HTML_NOSPACE_,
2702      soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_);
2703};
2704
2705/**
2706 * A helper for the Soy directive |normalizeHtmlNospace
2707 * @param {*} value Can be of any type but will be coerced to a string.
2708 * @return {string} The escaped text.
2709 */
2710soy.esc.$$normalizeHtmlNospaceHelper = function(value) {
2711  var str = String(value);
2712  return str.replace(
2713      soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_NOSPACE_,
2714      soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_);
2715};
2716
2717/**
2718 * A helper for the Soy directive |escapeJsString
2719 * @param {*} value Can be of any type but will be coerced to a string.
2720 * @return {string} The escaped text.
2721 */
2722soy.esc.$$escapeJsStringHelper = function(value) {
2723  var str = String(value);
2724  return str.replace(
2725      soy.esc.$$MATCHER_FOR_ESCAPE_JS_STRING_,
2726      soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_);
2727};
2728
2729/**
2730 * A helper for the Soy directive |escapeJsRegex
2731 * @param {*} value Can be of any type but will be coerced to 
2731a string.
2732 * @return {string} The escaped text.
2733 */
2734soy.esc.$$escapeJsRegexHelper = function(value) {
2735  var str = String(value);
2736  return str.replace(
2737      soy.esc.$$MATCHER_FOR_ESCAPE_JS_REGEX_,
2738      soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_);
2739};
2740
2741/**
2742 * A helper for the Soy directive |escapeCssString
2743 * @param {*} value Can be of any type but will be coerced to a string.
2744 * @return {string} The escaped text.
2745 */
2746soy.esc.$$escapeCssStringHelper = function(value) {
2747  var str = String(value);
2748  return str.replace(
2749      soy.esc.$$MATCHER_FOR_ESCAPE_CSS_STRING_,
2750      soy.esc.$$REPLACER_FOR_ESCAPE_CSS_STRING_);
2751};
2752
2753/**
2754 * A helper for the Soy directive |filterCssValue
2755 * @param {*} value Can be of any type but will be coerced to a string.
2756 * @return {string} The escaped text.
2757 */
2758soy.esc.$$filterCssValueHelper = function(value) {
2759  var str = String(value);
2760  if (!soy.esc.$$FILTER_FOR_FILTER_CSS_VALUE_.test(str)) {
2761    goog.asserts.fail('Bad value `%s` for |filterCssValue', [str]);
2762    return 'zSoyz';
2763  }
2764  return str;
2765};
2766
2767/**
2768 * A helper for the Soy directive |normalizeUri
2769 * @param {*} value Can be of any type but will be coerced to a string.
2770 * @return {string} The escaped text.
2771 */
2772soy.esc.$$normalizeUriHelper = function(value) {
2773  var str = String(value);
2774  return str.replace(
2775      soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_,
2776      soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_);
2777};
2778
2779/**
2780 * A helper for the Soy directive |filterNormalizeUri
2781 * @param {*} value Can be of any type but will be coerced to a string.
2782 * @return {string} The escaped text.
2783 */
2784soy.esc.$$filterNormalizeUriHelper = function(value) {
2785  var str = String(value);
2786  if (!soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_.test(str)) {
2787    goog.asserts.fail('Bad value `%s` for |filterNormalizeUri', [str]);
2788    return '#zSoyz';
2789  }
2790  return str.replace(
2791      soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_,
2792      soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_);
2793};
2794
2795/**
2796 * A helper for the Soy directive |filterHtmlAttributes
2797 * @param {*} value Can be of any type but will be coerced to a string.
2798 * @return {string} The escaped text.
2799 */
2800soy.esc.$$filterHtmlAttributesHelper = function(value) {
2801  var str = String(value);
2802  if (!soy.esc.$$FILTER_FOR_FILTER_HTML_ATTRIBUTES_.test(str)) {
2803    goog.asserts.fail('Bad value `%s` for |filterHtmlAttributes', [str]);
2804    return 'zSoyz';
2805  }
2806  return str;
2807};
2808
2809/**
2810 * A helper for the Soy directive |filterHtmlElementName
2811 * @param {*} value Can be of any type but will be coerced to a string.
2812 * @return {string} The escaped text.
2813 */
2814soy.esc.$$filterHtmlElementNameHelper = function(value) {
2815  var str = String(value);
2816  if (!soy.esc.$$FILTER_FOR_FILTER_HTML_ELEMENT_NAME_.test(str)) {
2817    goog.asserts.fail('Bad value `%s` for |filterHtmlElementName', [str]);
2818    return 'zSoyz';
2819  }
2820  return str;
2821};
2822
2823/**
2824 * Matches all tags, HTML comments, and DOCTYPEs in tag soup HTML.
2825 * By removing these, and replacing any '<' or '>' characters with
2826 * entities we guarantee that the result can be embedded into a
2827 * an attribute without introducing a tag boundary.
2828 *
2829 * @type {RegExp}
2830 * @private
2831 */
2832soy.esc.$$HTML_TAG_REGEX_ = /<(?:!|\/?([a-zA-Z][a-zA-Z0-9:\-]*))(?:[^>'"]|"[^"]*"|'[^']*')*>/g;
2833
2834/**
2835 * Matches all occurrences of '<'.
2836 *
2837 * @type {RegExp}
2838 * @private
2839 */
2840soy.esc.$$LT_REGEX_ = /</g;
2841
2842/**
2843 * Maps lower-case names of innocuous tags to 1.
2844 *
2845 * @type {Object.<string,number>}
2846 * @private
2847 */
2848soy.esc.$$SAFE_TAG_WHITELIST_ = {'b': 1, 'br': 1, 'em': 1, 'i': 1, 's': 1, 'sub': 1, 'sup': 1, 'u': 1};
2849
2850// END GENERATED CODE
2851
2852(()=>{
2853  var s=t=>{
2854    let e=t.dataset.target; let a=document.getElementById(e); a.classList.contains('collapse')?a.classList.remove('collapse'):a.classList.add('collapse');
2855  }; Array.from(document.getElementsByClassName('navbar-toggler')).forEach(t=>{
2856    t.onclick=()=>s(t);
2857  });
2858})();
2859
2860

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.