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 ? '­' : '<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_, '<'); 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, '['); 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.