diff options
author | Brian Picciano <mediocregopher@gmail.com> | 2021-07-31 11:35:39 -0600 |
---|---|---|
committer | Brian Picciano <mediocregopher@gmail.com> | 2021-07-31 11:35:39 -0600 |
commit | f1998c321a4eec6d75b58d84aa8610971bf21979 (patch) | |
tree | a90783eb296cc50e1c48433f241624f26b99be27 /static/src/assets/viz/2/goog/string | |
parent | 03a35dcc38b055f15df160bd300969e3b703d4b1 (diff) |
move static files into static sub-dir, refactor nix a bit
Diffstat (limited to 'static/src/assets/viz/2/goog/string')
-rw-r--r-- | static/src/assets/viz/2/goog/string/const.js | 186 | ||||
-rw-r--r-- | static/src/assets/viz/2/goog/string/string.js | 1641 | ||||
-rw-r--r-- | static/src/assets/viz/2/goog/string/stringbuffer.js | 103 | ||||
-rw-r--r-- | static/src/assets/viz/2/goog/string/stringformat.js | 221 | ||||
-rw-r--r-- | static/src/assets/viz/2/goog/string/typedstring.js | 48 |
5 files changed, 2199 insertions, 0 deletions
diff --git a/static/src/assets/viz/2/goog/string/const.js b/static/src/assets/viz/2/goog/string/const.js new file mode 100644 index 0000000..30bfc4e --- /dev/null +++ b/static/src/assets/viz/2/goog/string/const.js @@ -0,0 +1,186 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.string.Const'); + +goog.require('goog.asserts'); +goog.require('goog.string.TypedString'); + + + +/** + * Wrapper for compile-time-constant strings. + * + * Const is a wrapper for strings that can only be created from program + * constants (i.e., string literals). This property relies on a custom Closure + * compiler check that {@code goog.string.Const.from} is only invoked on + * compile-time-constant expressions. + * + * Const is useful in APIs whose correct and secure use requires that certain + * arguments are not attacker controlled: Compile-time constants are inherently + * under the control of the application and not under control of external + * attackers, and hence are safe to use in such contexts. + * + * Instances of this type must be created via its factory method + * {@code goog.string.Const.from} and not by invoking its constructor. The + * constructor intentionally takes no parameters and the type is immutable; + * hence only a default instance corresponding to the empty string can be + * obtained via constructor invocation. + * + * @see goog.string.Const#from + * @constructor + * @final + * @struct + * @implements {goog.string.TypedString} + */ +goog.string.Const = function() { + /** + * The wrapped value of this Const object. The field has a purposely ugly + * name to make (non-compiled) code that attempts to directly access this + * field stand out. + * @private {string} + */ + this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ = ''; + + /** + * A type marker used to implement additional run-time type checking. + * @see goog.string.Const#unwrap + * @const {!Object} + * @private + */ + this.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ = + goog.string.Const.TYPE_MARKER_; +}; + + +/** + * @override + * @const + */ +goog.string.Const.prototype.implementsGoogStringTypedString = true; + + +/** + * Returns this Const's value a string. + * + * IMPORTANT: In code where it is security-relevant that an object's type is + * indeed {@code goog.string.Const}, use {@code goog.string.Const.unwrap} + * instead of this method. + * + * @see goog.string.Const#unwrap + * @override + */ +goog.string.Const.prototype.getTypedStringValue = function() { + return this.stringConstValueWithSecurityContract__googStringSecurityPrivate_; +}; + + +/** + * Returns a debug-string representation of this value. + * + * To obtain the actual string value wrapped inside an object of this type, + * use {@code goog.string.Const.unwrap}. + * + * @see goog.string.Const#unwrap + * @override + */ +goog.string.Const.prototype.toString = function() { + return 'Const{' + + this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ + + '}'; +}; + + +/** + * Performs a runtime check that the provided object is indeed an instance + * of {@code goog.string.Const}, and returns its value. + * @param {!goog.string.Const} stringConst The object to extract from. + * @return {string} The Const object's contained string, unless the run-time + * type check fails. In that case, {@code unwrap} returns an innocuous + * string, or, if assertions are enabled, throws + * {@code goog.asserts.AssertionError}. + */ +goog.string.Const.unwrap = function(stringConst) { + // Perform additional run-time type-checking to ensure that stringConst is + // indeed an instance of the expected type. This provides some additional + // protection against security bugs due to application code that disables type + // checks. + if (stringConst instanceof goog.string.Const && + stringConst.constructor === goog.string.Const && + stringConst.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ === + goog.string.Const.TYPE_MARKER_) { + return stringConst + .stringConstValueWithSecurityContract__googStringSecurityPrivate_; + } else { + goog.asserts.fail( + 'expected object of type Const, got \'' + stringConst + '\''); + return 'type_error:Const'; + } +}; + + +/** + * Creates a Const object from a compile-time constant string. + * + * It is illegal to invoke this function on an expression whose + * compile-time-contant value cannot be determined by the Closure compiler. + * + * Correct invocations include, + * <pre> + * var s = goog.string.Const.from('hello'); + * var t = goog.string.Const.from('hello' + 'world'); + * </pre> + * + * In contrast, the following are illegal: + * <pre> + * var s = goog.string.Const.from(getHello()); + * var t = goog.string.Const.from('hello' + world); + * </pre> + * + * @param {string} s A constant string from which to create a Const. + * @return {!goog.string.Const} A Const object initialized to stringConst. + */ +goog.string.Const.from = function(s) { + return goog.string.Const.create__googStringSecurityPrivate_(s); +}; + + +/** + * Type marker for the Const type, used to implement additional run-time + * type checking. + * @const {!Object} + * @private + */ +goog.string.Const.TYPE_MARKER_ = {}; + + +/** + * Utility method to create Const instances. + * @param {string} s The string to initialize the Const object with. + * @return {!goog.string.Const} The initialized Const object. + * @private + */ +goog.string.Const.create__googStringSecurityPrivate_ = function(s) { + var stringConst = new goog.string.Const(); + stringConst.stringConstValueWithSecurityContract__googStringSecurityPrivate_ = + s; + return stringConst; +}; + + +/** + * A Const instance wrapping the empty string. + * @const {!goog.string.Const} + */ +goog.string.Const.EMPTY = goog.string.Const.from(''); diff --git a/static/src/assets/viz/2/goog/string/string.js b/static/src/assets/viz/2/goog/string/string.js new file mode 100644 index 0000000..7a10ae0 --- /dev/null +++ b/static/src/assets/viz/2/goog/string/string.js @@ -0,0 +1,1641 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for string manipulation. + * @author arv@google.com (Erik Arvidsson) + */ + + +/** + * Namespace for string utilities + */ +goog.provide('goog.string'); +goog.provide('goog.string.Unicode'); + + +/** + * @define {boolean} Enables HTML escaping of lowercase letter "e" which helps + * with detection of double-escaping as this letter is frequently used. + */ +goog.define('goog.string.DETECT_DOUBLE_ESCAPING', false); + + +/** + * @define {boolean} Whether to force non-dom html unescaping. + */ +goog.define('goog.string.FORCE_NON_DOM_HTML_UNESCAPING', false); + + +/** + * Common Unicode string characters. + * @enum {string} + */ +goog.string.Unicode = { + NBSP: '\xa0' +}; + + +/** + * Fast prefix-checker. + * @param {string} str The string to check. + * @param {string} prefix A string to look for at the start of {@code str}. + * @return {boolean} True if {@code str} begins with {@code prefix}. + */ +goog.string.startsWith = function(str, prefix) { + return str.lastIndexOf(prefix, 0) == 0; +}; + + +/** + * Fast suffix-checker. + * @param {string} str The string to check. + * @param {string} suffix A string to look for at the end of {@code str}. + * @return {boolean} True if {@code str} ends with {@code suffix}. + */ +goog.string.endsWith = function(str, suffix) { + var l = str.length - suffix.length; + return l >= 0 && str.indexOf(suffix, l) == l; +}; + + +/** + * Case-insensitive prefix-checker. + * @param {string} str The string to check. + * @param {string} prefix A string to look for at the end of {@code str}. + * @return {boolean} True if {@code str} begins with {@code prefix} (ignoring + * case). + */ +goog.string.caseInsensitiveStartsWith = function(str, prefix) { + return goog.string.caseInsensitiveCompare( + prefix, str.substr(0, prefix.length)) == 0; +}; + + +/** + * Case-insensitive suffix-checker. + * @param {string} str The string to check. + * @param {string} suffix A string to look for at the end of {@code str}. + * @return {boolean} True if {@code str} ends with {@code suffix} (ignoring + * case). + */ +goog.string.caseInsensitiveEndsWith = function(str, suffix) { + return ( + goog.string.caseInsensitiveCompare( + suffix, str.substr(str.length - suffix.length, suffix.length)) == 0); +}; + + +/** + * Case-insensitive equality checker. + * @param {string} str1 First string to check. + * @param {string} str2 Second string to check. + * @return {boolean} True if {@code str1} and {@code str2} are the same string, + * ignoring case. + */ +goog.string.caseInsensitiveEquals = function(str1, str2) { + return str1.toLowerCase() == str2.toLowerCase(); +}; + + +/** + * Does simple python-style string substitution. + * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog". + * @param {string} str The string containing the pattern. + * @param {...*} var_args The items to substitute into the pattern. + * @return {string} A copy of {@code str} in which each occurrence of + * {@code %s} has been replaced an argument from {@code var_args}. + */ +goog.string.subs = function(str, var_args) { + var splitParts = str.split('%s'); + var returnString = ''; + + var subsArguments = Array.prototype.slice.call(arguments, 1); + while (subsArguments.length && + // Replace up to the last split part. We are inserting in the + // positions between split parts. + splitParts.length > 1) { + returnString += splitParts.shift() + subsArguments.shift(); + } + + return returnString + splitParts.join('%s'); // Join unused '%s' +}; + + +/** + * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines + * and tabs) to a single space, and strips leading and trailing whitespace. + * @param {string} str Input string. + * @return {string} A copy of {@code str} with collapsed whitespace. + */ +goog.string.collapseWhitespace = function(str) { + // Since IE doesn't include non-breaking-space (0xa0) in their \s character + // class (as required by section 7.2 of the ECMAScript spec), we explicitly + // include it in the regexp to enforce consistent cross-browser behavior. + return str.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, ''); +}; + + +/** + * Checks if a string is empty or contains only whitespaces. + * @param {string} str The string to check. + * @return {boolean} Whether {@code str} is empty or whitespace only. + */ +goog.string.isEmptyOrWhitespace = function(str) { + // testing length == 0 first is actually slower in all browsers (about the + // same in Opera). + // Since IE doesn't include non-breaking-space (0xa0) in their \s character + // class (as required by section 7.2 of the ECMAScript spec), we explicitly + // include it in the regexp to enforce consistent cross-browser behavior. + return /^[\s\xa0]*$/.test(str); +}; + + +/** + * Checks if a string is empty. + * @param {string} str The string to check. + * @return {boolean} Whether {@code str} is empty. + */ +goog.string.isEmptyString = function(str) { + return str.length == 0; +}; + + +/** + * Checks if a string is empty or contains only whitespaces. + * + * @param {string} str The string to check. + * @return {boolean} Whether {@code str} is empty or whitespace only. + * @deprecated Use goog.string.isEmptyOrWhitespace instead. + */ +goog.string.isEmpty = goog.string.isEmptyOrWhitespace; + + +/** + * Checks if a string is null, undefined, empty or contains only whitespaces. + * @param {*} str The string to check. + * @return {boolean} Whether {@code str} is null, undefined, empty, or + * whitespace only. + * @deprecated Use goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str)) + * instead. + */ +goog.string.isEmptyOrWhitespaceSafe = function(str) { + return goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str)); +}; + + +/** + * Checks if a string is null, undefined, empty or contains only whitespaces. + * + * @param {*} str The string to check. + * @return {boolean} Whether {@code str} is null, undefined, empty, or + * whitespace only. + * @deprecated Use goog.string.isEmptyOrWhitespace instead. + */ +goog.string.isEmptySafe = goog.string.isEmptyOrWhitespaceSafe; + + +/** + * Checks if a string is all breaking whitespace. + * @param {string} str The string to check. + * @return {boolean} Whether the string is all breaking whitespace. + */ +goog.string.isBreakingWhitespace = function(str) { + return !/[^\t\n\r ]/.test(str); +}; + + +/** + * Checks if a string contains all letters. + * @param {string} str string to check. + * @return {boolean} True if {@code str} consists entirely of letters. + */ +goog.string.isAlpha = function(str) { + return !/[^a-zA-Z]/.test(str); +}; + + +/** + * Checks if a string contains only numbers. + * @param {*} str string to check. If not a string, it will be + * casted to one. + * @return {boolean} True if {@code str} is numeric. + */ +goog.string.isNumeric = function(str) { + return !/[^0-9]/.test(str); +}; + + +/** + * Checks if a string contains only numbers or letters. + * @param {string} str string to check. + * @return {boolean} True if {@code str} is alphanumeric. + */ +goog.string.isAlphaNumeric = function(str) { + return !/[^a-zA-Z0-9]/.test(str); +}; + + +/** + * Checks if a character is a space character. + * @param {string} ch Character to check. + * @return {boolean} True if {@code ch} is a space. + */ +goog.string.isSpace = function(ch) { + return ch == ' '; +}; + + +/** + * Checks if a character is a valid unicode character. + * @param {string} ch Character to check. + * @return {boolean} True if {@code ch} is a valid unicode character. + */ +goog.string.isUnicodeChar = function(ch) { + return ch.length == 1 && ch >= ' ' && ch <= '~' || + ch >= '\u0080' && ch <= '\uFFFD'; +}; + + +/** + * Takes a string and replaces newlines with a space. Multiple lines are + * replaced with a single space. + * @param {string} str The string from which to strip newlines. + * @return {string} A copy of {@code str} stripped of newlines. + */ +goog.string.stripNewlines = function(str) { + return str.replace(/(\r\n|\r|\n)+/g, ' '); +}; + + +/** + * Replaces Windows and Mac new lines with unix style: \r or \r\n with \n. + * @param {string} str The string to in which to canonicalize newlines. + * @return {string} {@code str} A copy of {@code} with canonicalized newlines. + */ +goog.string.canonicalizeNewlines = function(str) { + return str.replace(/(\r\n|\r|\n)/g, '\n'); +}; + + +/** + * Normalizes whitespace in a string, replacing all whitespace chars with + * a space. + * @param {string} str The string in which to normalize whitespace. + * @return {string} A copy of {@code str} with all whitespace normalized. + */ +goog.string.normalizeWhitespace = function(str) { + return str.replace(/\xa0|\s/g, ' '); +}; + + +/** + * Normalizes spaces in a string, replacing all consecutive spaces and tabs + * with a single space. Replaces non-breaking space with a space. + * @param {string} str The string in which to normalize spaces. + * @return {string} A copy of {@code str} with all consecutive spaces and tabs + * replaced with a single space. + */ +goog.string.normalizeSpaces = function(str) { + return str.replace(/\xa0|[ \t]+/g, ' '); +}; + + +/** + * Removes the breaking spaces from the left and right of the string and + * collapses the sequences of breaking spaces in the middle into single spaces. + * The original and the result strings render the same way in HTML. + * @param {string} str A string in which to collapse spaces. + * @return {string} Copy of the string with normalized breaking spaces. + */ +goog.string.collapseBreakingSpaces = function(str) { + return str.replace(/[\t\r\n ]+/g, ' ') + .replace(/^[\t\r\n ]+|[\t\r\n ]+$/g, ''); +}; + + +/** + * Trims white spaces to the left and right of a string. + * @param {string} str The string to trim. + * @return {string} A trimmed copy of {@code str}. + */ +goog.string.trim = + (goog.TRUSTED_SITE && String.prototype.trim) ? function(str) { + return str.trim(); + } : function(str) { + // Since IE doesn't include non-breaking-space (0xa0) in their \s + // character class (as required by section 7.2 of the ECMAScript spec), + // we explicitly include it in the regexp to enforce consistent + // cross-browser behavior. + return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''); + }; + + +/** + * Trims whitespaces at the left end of a string. + * @param {string} str The string to left trim. + * @return {string} A trimmed copy of {@code str}. + */ +goog.string.trimLeft = function(str) { + // Since IE doesn't include non-breaking-space (0xa0) in their \s character + // class (as required by section 7.2 of the ECMAScript spec), we explicitly + // include it in the regexp to enforce consistent cross-browser behavior. + return str.replace(/^[\s\xa0]+/, ''); +}; + + +/** + * Trims whitespaces at the right end of a string. + * @param {string} str The string to right trim. + * @return {string} A trimmed copy of {@code str}. + */ +goog.string.trimRight = function(str) { + // Since IE doesn't include non-breaking-space (0xa0) in their \s character + // class (as required by section 7.2 of the ECMAScript spec), we explicitly + // include it in the regexp to enforce consistent cross-browser behavior. + return str.replace(/[\s\xa0]+$/, ''); +}; + + +/** + * A string comparator that ignores case. + * -1 = str1 less than str2 + * 0 = str1 equals str2 + * 1 = str1 greater than str2 + * + * @param {string} str1 The string to compare. + * @param {string} str2 The string to compare {@code str1} to. + * @return {number} The comparator result, as described above. + */ +goog.string.caseInsensitiveCompare = function(str1, str2) { + var test1 = String(str1).toLowerCase(); + var test2 = String(str2).toLowerCase(); + + if (test1 < test2) { + return -1; + } else if (test1 == test2) { + return 0; + } else { + return 1; + } +}; + + +/** + * Compares two strings interpreting their numeric substrings as numbers. + * + * @param {string} str1 First string. + * @param {string} str2 Second string. + * @param {!RegExp} tokenizerRegExp Splits a string into substrings of + * non-negative integers, non-numeric characters and optionally fractional + * numbers starting with a decimal point. + * @return {number} Negative if str1 < str2, 0 is str1 == str2, positive if + * str1 > str2. + * @private + */ +goog.string.numberAwareCompare_ = function(str1, str2, tokenizerRegExp) { + if (str1 == str2) { + return 0; + } + if (!str1) { + return -1; + } + if (!str2) { + return 1; + } + + // Using match to split the entire string ahead of time turns out to be faster + // for most inputs than using RegExp.exec or iterating over each character. + var tokens1 = str1.toLowerCase().match(tokenizerRegExp); + var tokens2 = str2.toLowerCase().match(tokenizerRegExp); + + var count = Math.min(tokens1.length, tokens2.length); + + for (var i = 0; i < count; i++) { + var a = tokens1[i]; + var b = tokens2[i]; + + // Compare pairs of tokens, returning if one token sorts before the other. + if (a != b) { + // Only if both tokens are integers is a special comparison required. + // Decimal numbers are sorted as strings (e.g., '.09' < '.1'). + var num1 = parseInt(a, 10); + if (!isNaN(num1)) { + var num2 = parseInt(b, 10); + if (!isNaN(num2) && num1 - num2) { + return num1 - num2; + } + } + return a < b ? -1 : 1; + } + } + + // If one string is a substring of the other, the shorter string sorts first. + if (tokens1.length != tokens2.length) { + return tokens1.length - tokens2.length; + } + + // The two strings must be equivalent except for case (perfect equality is + // tested at the head of the function.) Revert to default ASCII string + // comparison to stabilize the sort. + return str1 < str2 ? -1 : 1; +}; + + +/** + * String comparison function that handles non-negative integer numbers in a + * way humans might expect. Using this function, the string 'File 2.jpg' sorts + * before 'File 10.jpg', and 'Version 1.9' before 'Version 1.10'. The comparison + * is mostly case-insensitive, though strings that are identical except for case + * are sorted with the upper-case strings before lower-case. + * + * This comparison function is up to 50x slower than either the default or the + * case-insensitive compare. It should not be used in time-critical code, but + * should be fast enough to sort several hundred short strings (like filenames) + * with a reasonable delay. + * + * @param {string} str1 The string to compare in a numerically sensitive way. + * @param {string} str2 The string to compare {@code str1} to. + * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than + * 0 if str1 > str2. + */ +goog.string.intAwareCompare = function(str1, str2) { + return goog.string.numberAwareCompare_(str1, str2, /\d+|\D+/g); +}; + + +/** + * String comparison function that handles non-negative integer and fractional + * numbers in a way humans might expect. Using this function, the string + * 'File 2.jpg' sorts before 'File 10.jpg', and '3.14' before '3.2'. Equivalent + * to {@link goog.string.intAwareCompare} apart from the way how it interprets + * dots. + * + * @param {string} str1 The string to compare in a numerically sensitive way. + * @param {string} str2 The string to compare {@code str1} to. + * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than + * 0 if str1 > str2. + */ +goog.string.floatAwareCompare = function(str1, str2) { + return goog.string.numberAwareCompare_(str1, str2, /\d+|\.\d+|\D+/g); +}; + + +/** + * Alias for {@link goog.string.floatAwareCompare}. + * + * @param {string} str1 + * @param {string} str2 + * @return {number} + */ +goog.string.numerateCompare = goog.string.floatAwareCompare; + + +/** + * URL-encodes a string + * @param {*} str The string to url-encode. + * @return {string} An encoded copy of {@code str} that is safe for urls. + * Note that '#', ':', and other characters used to delimit portions + * of URLs *will* be encoded. + */ +goog.string.urlEncode = function(str) { + return encodeURIComponent(String(str)); +}; + + +/** + * URL-decodes the string. We need to specially handle '+'s because + * the javascript library doesn't convert them to spaces. + * @param {string} str The string to url decode. + * @return {string} The decoded {@code str}. + */ +goog.string.urlDecode = function(str) { + return decodeURIComponent(str.replace(/\+/g, ' ')); +}; + + +/** + * Converts \n to <br>s or <br />s. + * @param {string} str The string in which to convert newlines. + * @param {boolean=} opt_xml Whether to use XML compatible tags. + * @return {string} A copy of {@code str} with converted newlines. + */ +goog.string.newLineToBr = function(str, opt_xml) { + return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>'); +}; + + +/** + * Escapes double quote '"' and single quote '\'' characters in addition to + * '&', '<', and '>' so that a string can be included in an HTML tag attribute + * value within double or single quotes. + * + * It should be noted that > doesn't need to be escaped for the HTML or XML to + * be valid, but it has been decided to escape it for consistency with other + * implementations. + * + * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the + * lowercase letter "e". + * + * NOTE(user): + * HtmlEscape is often called during the generation of large blocks of HTML. + * Using statics for the regular expressions and strings is an optimization + * that can more than half the amount of time IE spends in this function for + * large apps, since strings and regexes both contribute to GC allocations. + * + * Testing for the presence of a character before escaping increases the number + * of function calls, but actually provides a speed increase for the average + * case -- since the average case often doesn't require the escaping of all 4 + * characters and indexOf() is much cheaper than replace(). + * The worst case does suffer slightly from the additional calls, therefore the + * opt_isLikelyToContainHtmlChars option has been included for situations + * where all 4 HTML entities are very likely to be present and need escaping. + * + * Some benchmarks (times tended to fluctuate +-0.05ms): + * FireFox IE6 + * (no chars / average (mix of cases) / all 4 chars) + * no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80 + * indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84 + * indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85 + * + * An additional advantage of checking if replace actually needs to be called + * is a reduction in the number of object allocations, so as the size of the + * application grows the difference between the various methods would increase. + * + * @param {string} str string to be escaped. + * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see + * if the character needs replacing - use this option if you expect each of + * the characters to appear often. Leave false if you expect few html + * characters to occur in your strings, such as if you are escaping HTML. + * @return {string} An escaped copy of {@code str}. + */ +goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) { + + if (opt_isLikelyToContainHtmlChars) { + str = str.replace(goog.string.AMP_RE_, '&') + .replace(goog.string.LT_RE_, '<') + .replace(goog.string.GT_RE_, '>') + .replace(goog.string.QUOT_RE_, '"') + .replace(goog.string.SINGLE_QUOTE_RE_, ''') + .replace(goog.string.NULL_RE_, '�'); + if (goog.string.DETECT_DOUBLE_ESCAPING) { + str = str.replace(goog.string.E_RE_, 'e'); + } + return str; + + } else { + // quick test helps in the case when there are no chars to replace, in + // worst case this makes barely a difference to the time taken + if (!goog.string.ALL_RE_.test(str)) return str; + + // str.indexOf is faster than regex.test in this case + if (str.indexOf('&') != -1) { + str = str.replace(goog.string.AMP_RE_, '&'); + } + if (str.indexOf('<') != -1) { + str = str.replace(goog.string.LT_RE_, '<'); + } + if (str.indexOf('>') != -1) { + str = str.replace(goog.string.GT_RE_, '>'); + } + if (str.indexOf('"') != -1) { + str = str.replace(goog.string.QUOT_RE_, '"'); + } + if (str.indexOf('\'') != -1) { + str = str.replace(goog.string.SINGLE_QUOTE_RE_, '''); + } + if (str.indexOf('\x00') != -1) { + str = str.replace(goog.string.NULL_RE_, '�'); + } + if (goog.string.DETECT_DOUBLE_ESCAPING && str.indexOf('e') != -1) { + str = str.replace(goog.string.E_RE_, 'e'); + } + return str; + } +}; + + +/** + * Regular expression that matches an ampersand, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.AMP_RE_ = /&/g; + + +/** + * Regular expression that matches a less than sign, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.LT_RE_ = /</g; + + +/** + * Regular expression that matches a greater than sign, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.GT_RE_ = />/g; + + +/** + * Regular expression that matches a double quote, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.QUOT_RE_ = /"/g; + + +/** + * Regular expression that matches a single quote, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.SINGLE_QUOTE_RE_ = /'/g; + + +/** + * Regular expression that matches null character, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.NULL_RE_ = /\x00/g; + + +/** + * Regular expression that matches a lowercase letter "e", for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.E_RE_ = /e/g; + + +/** + * Regular expression that matches any character that needs to be escaped. + * @const {!RegExp} + * @private + */ +goog.string.ALL_RE_ = + (goog.string.DETECT_DOUBLE_ESCAPING ? /[\x00&<>"'e]/ : /[\x00&<>"']/); + + +/** + * Unescapes an HTML string. + * + * @param {string} str The string to unescape. + * @return {string} An unescaped copy of {@code str}. + */ +goog.string.unescapeEntities = function(str) { + if (goog.string.contains(str, '&')) { + // We are careful not to use a DOM if we do not have one or we explicitly + // requested non-DOM html unescaping. + if (!goog.string.FORCE_NON_DOM_HTML_UNESCAPING && + 'document' in goog.global) { + return goog.string.unescapeEntitiesUsingDom_(str); + } else { + // Fall back on pure XML entities + return goog.string.unescapePureXmlEntities_(str); + } + } + return str; +}; + + +/** + * Unescapes a HTML string using the provided document. + * + * @param {string} str The string to unescape. + * @param {!Document} document A document to use in escaping the string. + * @return {string} An unescaped copy of {@code str}. + */ +goog.string.unescapeEntitiesWithDocument = function(str, document) { + if (goog.string.contains(str, '&')) { + return goog.string.unescapeEntitiesUsingDom_(str, document); + } + return str; +}; + + +/** + * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric + * entities. This function is XSS-safe and whitespace-preserving. + * @private + * @param {string} str The string to unescape. + * @param {Document=} opt_document An optional document to use for creating + * elements. If this is not specified then the default window.document + * will be used. + * @return {string} The unescaped {@code str} string. + */ +goog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) { + /** @type {!Object<string, string>} */ + var seen = {'&': '&', '<': '<', '>': '>', '"': '"'}; + var div; + if (opt_document) { + div = opt_document.createElement('div'); + } else { + div = goog.global.document.createElement('div'); + } + // Match as many valid entity characters as possible. If the actual entity + // happens to be shorter, it will still work as innerHTML will return the + // trailing characters unchanged. Since the entity characters do not include + // open angle bracket, there is no chance of XSS from the innerHTML use. + // Since no whitespace is passed to innerHTML, whitespace is preserved. + return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) { + // Check for cached entity. + var value = seen[s]; + if (value) { + return value; + } + // Check for numeric entity. + if (entity.charAt(0) == '#') { + // Prefix with 0 so that hex entities (e.g. ) parse as hex numbers. + var n = Number('0' + entity.substr(1)); + if (!isNaN(n)) { + value = String.fromCharCode(n); + } + } + // Fall back to innerHTML otherwise. + if (!value) { + // Append a non-entity character to avoid a bug in Webkit that parses + // an invalid entity at the end of innerHTML text as the empty string. + div.innerHTML = s + ' '; + // Then remove the trailing character from the result. + value = div.firstChild.nodeValue.slice(0, -1); + } + // Cache and return. + return seen[s] = value; + }); +}; + + +/** + * Unescapes XML entities. + * @private + * @param {string} str The string to unescape. + * @return {string} An unescaped copy of {@code str}. + */ +goog.string.unescapePureXmlEntities_ = function(str) { + return str.replace(/&([^;]+);/g, function(s, entity) { + switch (entity) { + case 'amp': + return '&'; + case 'lt': + return '<'; + case 'gt': + return '>'; + case 'quot': + return '"'; + default: + if (entity.charAt(0) == '#') { + // Prefix with 0 so that hex entities (e.g. ) parse as hex. + var n = Number('0' + entity.substr(1)); + if (!isNaN(n)) { + return String.fromCharCode(n); + } + } + // For invalid entities we just return the entity + return s; + } + }); +}; + + +/** + * Regular expression that matches an HTML entity. + * See also HTML5: Tokenization / Tokenizing character references. + * @private + * @type {!RegExp} + */ +goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g; + + +/** + * Do escaping of whitespace to preserve spatial formatting. We use character + * entity #160 to make it safer for xml. + * @param {string} str The string in which to escape whitespace. + * @param {boolean=} opt_xml Whether to use XML compatible tags. + * @return {string} An escaped copy of {@code str}. + */ +goog.string.whitespaceEscape = function(str, opt_xml) { + // This doesn't use goog.string.preserveSpaces for backwards compatibility. + return goog.string.newLineToBr(str.replace(/ /g, '  '), opt_xml); +}; + + +/** + * Preserve spaces that would be otherwise collapsed in HTML by replacing them + * with non-breaking space Unicode characters. + * @param {string} str The string in which to preserve whitespace. + * @return {string} A copy of {@code str} with preserved whitespace. + */ +goog.string.preserveSpaces = function(str) { + return str.replace(/(^|[\n ]) /g, '$1' + goog.string.Unicode.NBSP); +}; + + +/** + * Strip quote characters around a string. The second argument is a string of + * characters to treat as quotes. This can be a single character or a string of + * multiple character and in that case each of those are treated as possible + * quote characters. For example: + * + * <pre> + * goog.string.stripQuotes('"abc"', '"`') --> 'abc' + * goog.string.stripQuotes('`abc`', '"`') --> 'abc' + * </pre> + * + * @param {string} str The string to strip. + * @param {string} quoteChars The quote characters to strip. + * @return {string} A copy of {@code str} without the quotes. + */ +goog.string.stripQuotes = function(str, quoteChars) { + var length = quoteChars.length; + for (var i = 0; i < length; i++) { + var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i); + if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) { + return str.substring(1, str.length - 1); + } + } + return str; +}; + + +/** + * Truncates a string to a certain length and adds '...' if necessary. The + * length also accounts for the ellipsis, so a maximum length of 10 and a string + * 'Hello World!' produces 'Hello W...'. + * @param {string} str The string to truncate. + * @param {number} chars Max number of characters. + * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped + * characters from being cut off in the middle. + * @return {string} The truncated {@code str} string. + */ +goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) { + if (opt_protectEscapedCharacters) { + str = goog.string.unescapeEntities(str); + } + + if (str.length > chars) { + str = str.substring(0, chars - 3) + '...'; + } + + if (opt_protectEscapedCharacters) { + str = goog.string.htmlEscape(str); + } + + return str; +}; + + +/** + * Truncate a string in the middle, adding "..." if necessary, + * and favoring the beginning of the string. + * @param {string} str The string to truncate the middle of. + * @param {number} chars Max number of characters. + * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped + * characters from being cutoff in the middle. + * @param {number=} opt_trailingChars Optional number of trailing characters to + * leave at the end of the string, instead of truncating as close to the + * middle as possible. + * @return {string} A truncated copy of {@code str}. + */ +goog.string.truncateMiddle = function( + str, chars, opt_protectEscapedCharacters, opt_trailingChars) { + if (opt_protectEscapedCharacters) { + str = goog.string.unescapeEntities(str); + } + + if (opt_trailingChars && str.length > chars) { + if (opt_trailingChars > chars) { + opt_trailingChars = chars; + } + var endPoint = str.length - opt_trailingChars; + var startPoint = chars - opt_trailingChars; + str = str.substring(0, startPoint) + '...' + str.substring(endPoint); + } else if (str.length > chars) { + // Favor the beginning of the string: + var half = Math.floor(chars / 2); + var endPos = str.length - half; + half += chars % 2; + str = str.substring(0, half) + '...' + str.substring(endPos); + } + + if (opt_protectEscapedCharacters) { + str = goog.string.htmlEscape(str); + } + + return str; +}; + + +/** + * Special chars that need to be escaped for goog.string.quote. + * @private {!Object<string, string>} + */ +goog.string.specialEscapeChars_ = { + '\0': '\\0', + '\b': '\\b', + '\f': '\\f', + '\n': '\\n', + '\r': '\\r', + '\t': '\\t', + '\x0B': '\\x0B', // '\v' is not supported in JScript + '"': '\\"', + '\\': '\\\\', + // To support the use case of embedding quoted strings inside of script + // tags, we have to make sure HTML comments and opening/closing script tags do + // not appear in the resulting string. The specific strings that must be + // escaped are documented at: + // http://www.w3.org/TR/html51/semantics.html#restrictions-for-contents-of-script-elements + '<': '\x3c' +}; + + +/** + * Character mappings used internally for goog.string.escapeChar. + * @private {!Object<string, string>} + */ +goog.string.jsEscapeCache_ = { + '\'': '\\\'' +}; + + +/** + * Encloses a string in double quotes and escapes characters so that the + * string is a valid JS string. The resulting string is safe to embed in + * `<script>` tags as "<" is escaped. + * @param {string} s The string to quote. + * @return {string} A copy of {@code s} surrounded by double quotes. + */ +goog.string.quote = function(s) { + s = String(s); + var sb = ['"']; + for (var i = 0; i < s.length; i++) { + var ch = s.charAt(i); + var cc = ch.charCodeAt(0); + sb[i + 1] = goog.string.specialEscapeChars_[ch] || + ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch)); + } + sb.push('"'); + return sb.join(''); +}; + + +/** + * Takes a string and returns the escaped string for that input string. + * @param {string} str The string to escape. + * @return {string} An escaped string representing {@code str}. + */ +goog.string.escapeString = function(str) { + var sb = []; + for (var i = 0; i < str.length; i++) { + sb[i] = goog.string.escapeChar(str.charAt(i)); + } + return sb.join(''); +}; + + +/** + * Takes a character and returns the escaped string for that character. For + * example escapeChar(String.fromCharCode(15)) -> "\\x0E". + * @param {string} c The character to escape. + * @return {string} An escaped string representing {@code c}. + */ +goog.string.escapeChar = function(c) { + if (c in goog.string.jsEscapeCache_) { + return goog.string.jsEscapeCache_[c]; + } + + if (c in goog.string.specialEscapeChars_) { + return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c]; + } + + var rv = c; + var cc = c.charCodeAt(0); + if (cc > 31 && cc < 127) { + rv = c; + } else { + // tab is 9 but handled above + if (cc < 256) { + rv = '\\x'; + if (cc < 16 || cc > 256) { + rv += '0'; + } + } else { + rv = '\\u'; + if (cc < 4096) { // \u1000 + rv += '0'; + } + } + rv += cc.toString(16).toUpperCase(); + } + + return goog.string.jsEscapeCache_[c] = rv; +}; + + +/** + * Determines whether a string contains a substring. + * @param {string} str The string to search. + * @param {string} subString The substring to search for. + * @return {boolean} Whether {@code str} contains {@code subString}. + */ +goog.string.contains = function(str, subString) { + return str.indexOf(subString) != -1; +}; + + +/** + * Determines whether a string contains a substring, ignoring case. + * @param {string} str The string to search. + * @param {string} subString The substring to search for. + * @return {boolean} Whether {@code str} contains {@code subString}. + */ +goog.string.caseInsensitiveContains = function(str, subString) { + return goog.string.contains(str.toLowerCase(), subString.toLowerCase()); +}; + + +/** + * Returns the non-overlapping occurrences of ss in s. + * If either s or ss evalutes to false, then returns zero. + * @param {string} s The string to look in. + * @param {string} ss The string to look for. + * @return {number} Number of occurrences of ss in s. + */ +goog.string.countOf = function(s, ss) { + return s && ss ? s.split(ss).length - 1 : 0; +}; + + +/** + * Removes a substring of a specified length at a specific + * index in a string. + * @param {string} s The base string from which to remove. + * @param {number} index The index at which to remove the substring. + * @param {number} stringLength The length of the substring to remove. + * @return {string} A copy of {@code s} with the substring removed or the full + * string if nothing is removed or the input is invalid. + */ +goog.string.removeAt = function(s, index, stringLength) { + var resultStr = s; + // If the index is greater or equal to 0 then remove substring + if (index >= 0 && index < s.length && stringLength > 0) { + resultStr = s.substr(0, index) + + s.substr(index + stringLength, s.length - index - stringLength); + } + return resultStr; +}; + + +/** + * Removes the first occurrence of a substring from a string. + * @param {string} str The base string from which to remove. + * @param {string} substr The string to remove. + * @return {string} A copy of {@code str} with {@code substr} removed or the + * full string if nothing is removed. + */ +goog.string.remove = function(str, substr) { + return str.replace(substr, ''); +}; + + +/** + * Removes all occurrences of a substring from a string. + * @param {string} s The base string from which to remove. + * @param {string} ss The string to remove. + * @return {string} A copy of {@code s} with {@code ss} removed or the full + * string if nothing is removed. + */ +goog.string.removeAll = function(s, ss) { + var re = new RegExp(goog.string.regExpEscape(ss), 'g'); + return s.replace(re, ''); +}; + + +/** + * Replaces all occurrences of a substring of a string with a new substring. + * @param {string} s The base string from which to remove. + * @param {string} ss The string to replace. + * @param {string} replacement The replacement string. + * @return {string} A copy of {@code s} with {@code ss} replaced by + * {@code replacement} or the original string if nothing is replaced. + */ +goog.string.replaceAll = function(s, ss, replacement) { + var re = new RegExp(goog.string.regExpEscape(ss), 'g'); + return s.replace(re, replacement.replace(/\$/g, '$$$$')); +}; + + +/** + * Escapes characters in the string that are not safe to use in a RegExp. + * @param {*} s The string to escape. If not a string, it will be casted + * to one. + * @return {string} A RegExp safe, escaped copy of {@code s}. + */ +goog.string.regExpEscape = function(s) { + return String(s) + .replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1') + .replace(/\x08/g, '\\x08'); +}; + + +/** + * Repeats a string n times. + * @param {string} string The string to repeat. + * @param {number} length The number of times to repeat. + * @return {string} A string containing {@code length} repetitions of + * {@code string}. + */ +goog.string.repeat = (String.prototype.repeat) ? function(string, length) { + // The native method is over 100 times faster than the alternative. + return string.repeat(length); +} : function(string, length) { + return new Array(length + 1).join(string); +}; + + +/** + * Pads number to given length and optionally rounds it to a given precision. + * For example: + * <pre>padNumber(1.25, 2, 3) -> '01.250' + * padNumber(1.25, 2) -> '01.25' + * padNumber(1.25, 2, 1) -> '01.3' + * padNumber(1.25, 0) -> '1.25'</pre> + * + * @param {number} num The number to pad. + * @param {number} length The desired length. + * @param {number=} opt_precision The desired precision. + * @return {string} {@code num} as a string with the given options. + */ +goog.string.padNumber = function(num, length, opt_precision) { + var s = goog.isDef(opt_precision) ? num.toFixed(opt_precision) : String(num); + var index = s.indexOf('.'); + if (index == -1) { + index = s.length; + } + return goog.string.repeat('0', Math.max(0, length - index)) + s; +}; + + +/** + * Returns a string representation of the given object, with + * null and undefined being returned as the empty string. + * + * @param {*} obj The object to convert. + * @return {string} A string representation of the {@code obj}. + */ +goog.string.makeSafe = function(obj) { + return obj == null ? '' : String(obj); +}; + + +/** + * Concatenates string expressions. This is useful + * since some browsers are very inefficient when it comes to using plus to + * concat strings. Be careful when using null and undefined here since + * these will not be included in the result. If you need to represent these + * be sure to cast the argument to a String first. + * For example: + * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd' + * buildString(null, undefined) -> '' + * </pre> + * @param {...*} var_args A list of strings to concatenate. If not a string, + * it will be casted to one. + * @return {string} The concatenation of {@code var_args}. + */ +goog.string.buildString = function(var_args) { + return Array.prototype.join.call(arguments, ''); +}; + + +/** + * Returns a string with at least 64-bits of randomness. + * + * Doesn't trust Javascript's random function entirely. Uses a combination of + * random and current timestamp, and then encodes the string in base-36 to + * make it shorter. + * + * @return {string} A random string, e.g. sn1s7vb4gcic. + */ +goog.string.getRandomString = function() { + var x = 2147483648; + return Math.floor(Math.random() * x).toString(36) + + Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36); +}; + + +/** + * Compares two version numbers. + * + * @param {string|number} version1 Version of first item. + * @param {string|number} version2 Version of second item. + * + * @return {number} 1 if {@code version1} is higher. + * 0 if arguments are equal. + * -1 if {@code version2} is higher. + */ +goog.string.compareVersions = function(version1, version2) { + var order = 0; + // Trim leading and trailing whitespace and split the versions into + // subversions. + var v1Subs = goog.string.trim(String(version1)).split('.'); + var v2Subs = goog.string.trim(String(version2)).split('.'); + var subCount = Math.max(v1Subs.length, v2Subs.length); + + // Iterate over the subversions, as long as they appear to be equivalent. + for (var subIdx = 0; order == 0 && subIdx < subCount; subIdx++) { + var v1Sub = v1Subs[subIdx] || ''; + var v2Sub = v2Subs[subIdx] || ''; + + do { + // Split the subversions into pairs of numbers and qualifiers (like 'b'). + // Two different RegExp objects are use to make it clear the code + // is side-effect free + var v1Comp = /(\d*)(\D*)(.*)/.exec(v1Sub) || ['', '', '', '']; + var v2Comp = /(\d*)(\D*)(.*)/.exec(v2Sub) || ['', '', '', '']; + // Break if there are no more matches. + if (v1Comp[0].length == 0 && v2Comp[0].length == 0) { + break; + } + + // Parse the numeric part of the subversion. A missing number is + // equivalent to 0. + var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10); + var v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10); + + // Compare the subversion components. The number has the highest + // precedence. Next, if the numbers are equal, a subversion without any + // qualifier is always higher than a subversion with any qualifier. Next, + // the qualifiers are compared as strings. + order = goog.string.compareElements_(v1CompNum, v2CompNum) || + goog.string.compareElements_( + v1Comp[2].length == 0, v2Comp[2].length == 0) || + goog.string.compareElements_(v1Comp[2], v2Comp[2]); + // Stop as soon as an inequality is discovered. + + v1Sub = v1Comp[3]; + v2Sub = v2Comp[3]; + } while (order == 0); + } + + return order; +}; + + +/** + * Compares elements of a version number. + * + * @param {string|number|boolean} left An element from a version number. + * @param {string|number|boolean} right An element from a version number. + * + * @return {number} 1 if {@code left} is higher. + * 0 if arguments are equal. + * -1 if {@code right} is higher. + * @private + */ +goog.string.compareElements_ = function(left, right) { + if (left < right) { + return -1; + } else if (left > right) { + return 1; + } + return 0; +}; + + +/** + * String hash function similar to java.lang.String.hashCode(). + * The hash code for a string is computed as + * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1], + * where s[i] is the ith character of the string and n is the length of + * the string. We mod the result to make it between 0 (inclusive) and 2^32 + * (exclusive). + * @param {string} str A string. + * @return {number} Hash value for {@code str}, between 0 (inclusive) and 2^32 + * (exclusive). The empty string returns 0. + */ +goog.string.hashCode = function(str) { + var result = 0; + for (var i = 0; i < str.length; ++i) { + // Normalize to 4 byte range, 0 ... 2^32. + result = (31 * result + str.charCodeAt(i)) >>> 0; + } + return result; +}; + + +/** + * The most recent unique ID. |0 is equivalent to Math.floor in this case. + * @type {number} + * @private + */ +goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0; + + +/** + * Generates and returns a string which is unique in the current document. + * This is useful, for example, to create unique IDs for DOM elements. + * @return {string} A unique id. + */ +goog.string.createUniqueString = function() { + return 'goog_' + goog.string.uniqueStringCounter_++; +}; + + +/** + * Converts the supplied string to a number, which may be Infinity or NaN. + * This function strips whitespace: (toNumber(' 123') === 123) + * This function accepts scientific notation: (toNumber('1e1') === 10) + * + * This is better than Javascript's built-in conversions because, sadly: + * (Number(' ') === 0) and (parseFloat('123a') === 123) + * + * @param {string} str The string to convert. + * @return {number} The number the supplied string represents, or NaN. + */ +goog.string.toNumber = function(str) { + var num = Number(str); + if (num == 0 && goog.string.isEmptyOrWhitespace(str)) { + return NaN; + } + return num; +}; + + +/** + * Returns whether the given string is lower camel case (e.g. "isFooBar"). + * + * Note that this assumes the string is entirely letters. + * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms + * + * @param {string} str String to test. + * @return {boolean} Whether the string is lower camel case. + */ +goog.string.isLowerCamelCase = function(str) { + return /^[a-z]+([A-Z][a-z]*)*$/.test(str); +}; + + +/** + * Returns whether the given string is upper camel case (e.g. "FooBarBaz"). + * + * Note that this assumes the string is entirely letters. + * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms + * + * @param {string} str String to test. + * @return {boolean} Whether the string is upper camel case. + */ +goog.string.isUpperCamelCase = function(str) { + return /^([A-Z][a-z]*)+$/.test(str); +}; + + +/** + * Converts a string from selector-case to camelCase (e.g. from + * "multi-part-string" to "multiPartString"), useful for converting + * CSS selectors and HTML dataset keys to their equivalent JS properties. + * @param {string} str The string in selector-case form. + * @return {string} The string in camelCase form. + */ +goog.string.toCamelCase = function(str) { + return String(str).replace( + /\-([a-z])/g, function(all, match) { return match.toUpperCase(); }); +}; + + +/** + * Converts a string from camelCase to selector-case (e.g. from + * "multiPartString" to "multi-part-string"), useful for converting JS + * style and dataset properties to equivalent CSS selectors and HTML keys. + * @param {string} str The string in camelCase form. + * @return {string} The string in selector-case form. + */ +goog.string.toSelectorCase = function(str) { + return String(str).replace(/([A-Z])/g, '-$1').toLowerCase(); +}; + + +/** + * Converts a string into TitleCase. First character of the string is always + * capitalized in addition to the first letter of every subsequent word. + * Words are delimited by one or more whitespaces by default. Custom delimiters + * can optionally be specified to replace the default, which doesn't preserve + * whitespace delimiters and instead must be explicitly included if needed. + * + * Default delimiter => " ": + * goog.string.toTitleCase('oneTwoThree') => 'OneTwoThree' + * goog.string.toTitleCase('one two three') => 'One Two Three' + * goog.string.toTitleCase(' one two ') => ' One Two ' + * goog.string.toTitleCase('one_two_three') => 'One_two_three' + * goog.string.toTitleCase('one-two-three') => 'One-two-three' + * + * Custom delimiter => "_-.": + * goog.string.toTitleCase('oneTwoThree', '_-.') => 'OneTwoThree' + * goog.string.toTitleCase('one two three', '_-.') => 'One two three' + * goog.string.toTitleCase(' one two ', '_-.') => ' one two ' + * goog.string.toTitleCase('one_two_three', '_-.') => 'One_Two_Three' + * goog.string.toTitleCase('one-two-three', '_-.') => 'One-Two-Three' + * goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three' + * goog.string.toTitleCase('one. two. three', '_-.') => 'One. two. three' + * goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three' + * + * @param {string} str String value in camelCase form. + * @param {string=} opt_delimiters Custom delimiter character set used to + * distinguish words in the string value. Each character represents a + * single delimiter. When provided, default whitespace delimiter is + * overridden and must be explicitly included if needed. + * @return {string} String value in TitleCase form. + */ +goog.string.toTitleCase = function(str, opt_delimiters) { + var delimiters = goog.isString(opt_delimiters) ? + goog.string.regExpEscape(opt_delimiters) : + '\\s'; + + // For IE8, we need to prevent using an empty character set. Otherwise, + // incorrect matching will occur. + delimiters = delimiters ? '|[' + delimiters + ']+' : ''; + + var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g'); + return str.replace( + regexp, function(all, p1, p2) { return p1 + p2.toUpperCase(); }); +}; + + +/** + * Capitalizes a string, i.e. converts the first letter to uppercase + * and all other letters to lowercase, e.g.: + * + * goog.string.capitalize('one') => 'One' + * goog.string.capitalize('ONE') => 'One' + * goog.string.capitalize('one two') => 'One two' + * + * Note that this function does not trim initial whitespace. + * + * @param {string} str String value to capitalize. + * @return {string} String value with first letter in uppercase. + */ +goog.string.capitalize = function(str) { + return String(str.charAt(0)).toUpperCase() + + String(str.substr(1)).toLowerCase(); +}; + + +/** + * Parse a string in decimal or hexidecimal ('0xFFFF') form. + * + * To parse a particular radix, please use parseInt(string, radix) directly. See + * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt + * + * This is a wrapper for the built-in parseInt function that will only parse + * numbers as base 10 or base 16. Some JS implementations assume strings + * starting with "0" are intended to be octal. ES3 allowed but discouraged + * this behavior. ES5 forbids it. This function emulates the ES5 behavior. + * + * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj + * + * @param {string|number|null|undefined} value The value to be parsed. + * @return {number} The number, parsed. If the string failed to parse, this + * will be NaN. + */ +goog.string.parseInt = function(value) { + // Force finite numbers to strings. + if (isFinite(value)) { + value = String(value); + } + + if (goog.isString(value)) { + // If the string starts with '0x' or '-0x', parse as hex. + return /^\s*-?0x/i.test(value) ? parseInt(value, 16) : parseInt(value, 10); + } + + return NaN; +}; + + +/** + * Splits a string on a separator a limited number of times. + * + * This implementation is more similar to Python or Java, where the limit + * parameter specifies the maximum number of splits rather than truncating + * the number of results. + * + * See http://docs.python.org/2/library/stdtypes.html#str.split + * See JavaDoc: http://goo.gl/F2AsY + * See Mozilla reference: http://goo.gl/dZdZs + * + * @param {string} str String to split. + * @param {string} separator The separator. + * @param {number} limit The limit to the number of splits. The resulting array + * will have a maximum length of limit+1. Negative numbers are the same + * as zero. + * @return {!Array<string>} The string, split. + */ +goog.string.splitLimit = function(str, separator, limit) { + var parts = str.split(separator); + var returnVal = []; + + // Only continue doing this while we haven't hit the limit and we have + // parts left. + while (limit > 0 && parts.length) { + returnVal.push(parts.shift()); + limit--; + } + + // If there are remaining parts, append them to the end. + if (parts.length) { + returnVal.push(parts.join(separator)); + } + + return returnVal; +}; + + +/** + * Finds the characters to the right of the last instance of any separator + * + * This function is similar to goog.string.path.baseName, except it can take a + * list of characters to split the string on. It will return the rightmost + * grouping of characters to the right of any separator as a left-to-right + * oriented string. + * + * @see goog.string.path.baseName + * @param {string} str The string + * @param {string|!Array<string>} separators A list of separator characters + * @return {string} The last part of the string with respect to the separators + */ +goog.string.lastComponent = function(str, separators) { + if (!separators) { + return str; + } else if (typeof separators == 'string') { + separators = [separators]; + } + + var lastSeparatorIndex = -1; + for (var i = 0; i < separators.length; i++) { + if (separators[i] == '') { + continue; + } + var currentSeparatorIndex = str.lastIndexOf(separators[i]); + if (currentSeparatorIndex > lastSeparatorIndex) { + lastSeparatorIndex = currentSeparatorIndex; + } + } + if (lastSeparatorIndex == -1) { + return str; + } + return str.slice(lastSeparatorIndex + 1); +}; + + +/** + * Computes the Levenshtein edit distance between two strings. + * @param {string} a + * @param {string} b + * @return {number} The edit distance between the two strings. + */ +goog.string.editDistance = function(a, b) { + var v0 = []; + var v1 = []; + + if (a == b) { + return 0; + } + + if (!a.length || !b.length) { + return Math.max(a.length, b.length); + } + + for (var i = 0; i < b.length + 1; i++) { + v0[i] = i; + } + + for (var i = 0; i < a.length; i++) { + v1[0] = i + 1; + + for (var j = 0; j < b.length; j++) { + var cost = Number(a[i] != b[j]); + // Cost for the substring is the minimum of adding one character, removing + // one character, or a swap. + v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost); + } + + for (var j = 0; j < v0.length; j++) { + v0[j] = v1[j]; + } + } + + return v1[b.length]; +}; diff --git a/static/src/assets/viz/2/goog/string/stringbuffer.js b/static/src/assets/viz/2/goog/string/stringbuffer.js new file mode 100644 index 0000000..478b08b --- /dev/null +++ b/static/src/assets/viz/2/goog/string/stringbuffer.js @@ -0,0 +1,103 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utility for fast string concatenation. + */ + +goog.provide('goog.string.StringBuffer'); + + + +/** + * Utility class to facilitate string concatenation. + * + * @param {*=} opt_a1 Optional first initial item to append. + * @param {...*} var_args Other initial items to + * append, e.g., new goog.string.StringBuffer('foo', 'bar'). + * @constructor + */ +goog.string.StringBuffer = function(opt_a1, var_args) { + if (opt_a1 != null) { + this.append.apply(this, arguments); + } +}; + + +/** + * Internal buffer for the string to be concatenated. + * @type {string} + * @private + */ +goog.string.StringBuffer.prototype.buffer_ = ''; + + +/** + * Sets the contents of the string buffer object, replacing what's currently + * there. + * + * @param {*} s String to set. + */ +goog.string.StringBuffer.prototype.set = function(s) { + this.buffer_ = '' + s; +}; + + +/** + * Appends one or more items to the buffer. + * + * Calling this with null, undefined, or empty arguments is an error. + * + * @param {*} a1 Required first string. + * @param {*=} opt_a2 Optional second string. + * @param {...?} var_args Other items to append, + * e.g., sb.append('foo', 'bar', 'baz'). + * @return {!goog.string.StringBuffer} This same StringBuffer object. + * @suppress {duplicate} + */ +goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) { + // Use a1 directly to avoid arguments instantiation for single-arg case. + this.buffer_ += String(a1); + if (opt_a2 != null) { // second argument is undefined (null == undefined) + for (var i = 1; i < arguments.length; i++) { + this.buffer_ += arguments[i]; + } + } + return this; +}; + + +/** + * Clears the internal buffer. + */ +goog.string.StringBuffer.prototype.clear = function() { + this.buffer_ = ''; +}; + + +/** + * @return {number} the length of the current contents of the buffer. + */ +goog.string.StringBuffer.prototype.getLength = function() { + return this.buffer_.length; +}; + + +/** + * @return {string} The concatenated string. + * @override + */ +goog.string.StringBuffer.prototype.toString = function() { + return this.buffer_; +}; diff --git a/static/src/assets/viz/2/goog/string/stringformat.js b/static/src/assets/viz/2/goog/string/stringformat.js new file mode 100644 index 0000000..58d7af9 --- /dev/null +++ b/static/src/assets/viz/2/goog/string/stringformat.js @@ -0,0 +1,221 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Implementation of sprintf-like, python-%-operator-like, + * .NET-String.Format-like functionality. Uses JS string's replace method to + * extract format specifiers and sends those specifiers to a handler function, + * which then, based on conversion type part of the specifier, calls the + * appropriate function to handle the specific conversion. + * For specific functionality implemented, look at formatRe below, or look + * at the tests. + */ + +goog.provide('goog.string.format'); + +goog.require('goog.string'); + + +/** + * Performs sprintf-like conversion, i.e. puts the values in a template. + * DO NOT use it instead of built-in conversions in simple cases such as + * 'Cost: %.2f' as it would introduce unnecessary latency opposed to + * 'Cost: ' + cost.toFixed(2). + * @param {string} formatString Template string containing % specifiers. + * @param {...string|number} var_args Values formatString is to be filled with. + * @return {string} Formatted string. + */ +goog.string.format = function(formatString, var_args) { + + // Convert the arguments to an array (MDC recommended way). + var args = Array.prototype.slice.call(arguments); + + // Try to get the template. + var template = args.shift(); + if (typeof template == 'undefined') { + throw Error('[goog.string.format] Template required'); + } + + // This re is used for matching, it also defines what is supported. + var formatRe = /%([0\-\ \+]*)(\d+)?(\.(\d+))?([%sfdiu])/g; + + /** + * Chooses which conversion function to call based on type conversion + * specifier. + * @param {string} match Contains the re matched string. + * @param {string} flags Formatting flags. + * @param {string} width Replacement string minimum width. + * @param {string} dotp Matched precision including a dot. + * @param {string} precision Specifies floating point precision. + * @param {string} type Type conversion specifier. + * @param {string} offset Matching location in the original string. + * @param {string} wholeString Has the actualString being searched. + * @return {string} Formatted parameter. + */ + function replacerDemuxer( + match, flags, width, dotp, precision, type, offset, wholeString) { + // The % is too simple and doesn't take an argument. + if (type == '%') { + return '%'; + } + + // Try to get the actual value from parent function. + var value = args.shift(); + + // If we didn't get any arguments, fail. + if (typeof value == 'undefined') { + throw Error('[goog.string.format] Not enough arguments'); + } + + // Patch the value argument to the beginning of our type specific call. + arguments[0] = value; + + return goog.string.format.demuxes_[type].apply(null, arguments); + } + + return template.replace(formatRe, replacerDemuxer); +}; + + +/** + * Contains various conversion functions (to be filled in later on). + * @private {!Object} + */ +goog.string.format.demuxes_ = {}; + + +/** + * Processes %s conversion specifier. + * @param {string} value Contains the formatRe matched string. + * @param {string} flags Formatting flags. + * @param {string} width Replacement string minimum width. + * @param {string} dotp Matched precision including a dot. + * @param {string} precision Specifies floating point precision. + * @param {string} type Type conversion specifier. + * @param {string} offset Matching location in the original string. + * @param {string} wholeString Has the actualString being searched. + * @return {string} Replacement string. + */ +goog.string.format.demuxes_['s'] = function( + value, flags, width, dotp, precision, type, offset, wholeString) { + var replacement = value; + // If no padding is necessary we're done. + // The check for '' is necessary because Firefox incorrectly provides the + // empty string instead of undefined for non-participating capture groups, + // and isNaN('') == false. + if (isNaN(width) || width == '' || replacement.length >= Number(width)) { + return replacement; + } + + // Otherwise we should find out where to put spaces. + if (flags.indexOf('-', 0) > -1) { + replacement = replacement + + goog.string.repeat(' ', Number(width) - replacement.length); + } else { + replacement = goog.string.repeat(' ', Number(width) - replacement.length) + + replacement; + } + return replacement; +}; + + +/** + * Processes %f conversion specifier. + * @param {string} value Contains the formatRe matched string. + * @param {string} flags Formatting flags. + * @param {string} width Replacement string minimum width. + * @param {string} dotp Matched precision including a dot. + * @param {string} precision Specifies floating point precision. + * @param {string} type Type conversion specifier. + * @param {string} offset Matching location in the original string. + * @param {string} wholeString Has the actualString being searched. + * @return {string} Replacement string. + */ +goog.string.format.demuxes_['f'] = function( + value, flags, width, dotp, precision, type, offset, wholeString) { + + var replacement = value.toString(); + + // The check for '' is necessary because Firefox incorrectly provides the + // empty string instead of undefined for non-participating capture groups, + // and isNaN('') == false. + if (!(isNaN(precision) || precision == '')) { + replacement = parseFloat(value).toFixed(precision); + } + + // Generates sign string that will be attached to the replacement. + var sign; + if (Number(value) < 0) { + sign = '-'; + } else if (flags.indexOf('+') >= 0) { + sign = '+'; + } else if (flags.indexOf(' ') >= 0) { + sign = ' '; + } else { + sign = ''; + } + + if (Number(value) >= 0) { + replacement = sign + replacement; + } + + // If no padding is necessary we're done. + if (isNaN(width) || replacement.length >= Number(width)) { + return replacement; + } + + // We need a clean signless replacement to start with + replacement = isNaN(precision) ? Math.abs(Number(value)).toString() : + Math.abs(Number(value)).toFixed(precision); + + var padCount = Number(width) - replacement.length - sign.length; + + // Find out which side to pad, and if it's left side, then which character to + // pad, and set the sign on the left and padding in the middle. + if (flags.indexOf('-', 0) >= 0) { + replacement = sign + replacement + goog.string.repeat(' ', padCount); + } else { + // Decides which character to pad. + var paddingChar = (flags.indexOf('0', 0) >= 0) ? '0' : ' '; + replacement = + sign + goog.string.repeat(paddingChar, padCount) + replacement; + } + + return replacement; +}; + + +/** + * Processes %d conversion specifier. + * @param {string} value Contains the formatRe matched string. + * @param {string} flags Formatting flags. + * @param {string} width Replacement string minimum width. + * @param {string} dotp Matched precision including a dot. + * @param {string} precision Specifies floating point precision. + * @param {string} type Type conversion specifier. + * @param {string} offset Matching location in the original string. + * @param {string} wholeString Has the actualString being searched. + * @return {string} Replacement string. + */ +goog.string.format.demuxes_['d'] = function( + value, flags, width, dotp, precision, type, offset, wholeString) { + return goog.string.format.demuxes_['f']( + parseInt(value, 10) /* value */, flags, width, dotp, 0 /* precision */, + type, offset, wholeString); +}; + + +// These are additional aliases, for integer conversion. +goog.string.format.demuxes_['i'] = goog.string.format.demuxes_['d']; +goog.string.format.demuxes_['u'] = goog.string.format.demuxes_['d']; diff --git a/static/src/assets/viz/2/goog/string/typedstring.js b/static/src/assets/viz/2/goog/string/typedstring.js new file mode 100644 index 0000000..d0d7bd9 --- /dev/null +++ b/static/src/assets/viz/2/goog/string/typedstring.js @@ -0,0 +1,48 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.string.TypedString'); + + + +/** + * Wrapper for strings that conform to a data type or language. + * + * Implementations of this interface are wrappers for strings, and typically + * associate a type contract with the wrapped string. Concrete implementations + * of this interface may choose to implement additional run-time type checking, + * see for example {@code goog.html.SafeHtml}. If available, client code that + * needs to ensure type membership of an object should use the type's function + * to assert type membership, such as {@code goog.html.SafeHtml.unwrap}. + * @interface + */ +goog.string.TypedString = function() {}; + + +/** + * Interface marker of the TypedString interface. + * + * This property can be used to determine at runtime whether or not an object + * implements this interface. All implementations of this interface set this + * property to {@code true}. + * @type {boolean} + */ +goog.string.TypedString.prototype.implementsGoogStringTypedString; + + +/** + * Retrieves this wrapped string's value. + * @return {string} The wrapped string's value. + */ +goog.string.TypedString.prototype.getTypedStringValue; |