Path: blob/trunk/third_party/closure/goog/string/internal.js
4501 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview String functions called from Closure packages that couldn't8* depend on each other. Outside Closure, use goog.string function which9* delegate to these.10*/111213goog.provide('goog.string.internal');141516/**17* Fast prefix-checker.18* @param {string} str The string to check.19* @param {string} prefix A string to look for at the start of `str`.20* @return {boolean} True if `str` begins with `prefix`.21* @see goog.string.startsWith22*/23goog.string.internal.startsWith = function(str, prefix) {24'use strict';25return str.lastIndexOf(prefix, 0) == 0;26};272829/**30* Fast suffix-checker.31* @param {string} str The string to check.32* @param {string} suffix A string to look for at the end of `str`.33* @return {boolean} True if `str` ends with `suffix`.34* @see goog.string.endsWith35*/36goog.string.internal.endsWith = function(str, suffix) {37'use strict';38const l = str.length - suffix.length;39return l >= 0 && str.indexOf(suffix, l) == l;40};414243/**44* Case-insensitive prefix-checker.45* @param {string} str The string to check.46* @param {string} prefix A string to look for at the end of `str`.47* @return {boolean} True if `str` begins with `prefix` (ignoring48* case).49* @see goog.string.caseInsensitiveStartsWith50*/51goog.string.internal.caseInsensitiveStartsWith = function(str, prefix) {52'use strict';53return (54goog.string.internal.caseInsensitiveCompare(55prefix, str.slice(0, prefix.length)) == 0);56};575859/**60* Case-insensitive suffix-checker.61* @param {string} str The string to check.62* @param {string} suffix A string to look for at the end of `str`.63* @return {boolean} True if `str` ends with `suffix` (ignoring64* case).65* @see goog.string.caseInsensitiveEndsWith66*/67goog.string.internal.caseInsensitiveEndsWith = function(str, suffix) {68'use strict';69return (70goog.string.internal.caseInsensitiveCompare(71suffix, str.slice(str.length - suffix.length)) == 0);72};737475/**76* Case-insensitive equality checker.77* @param {string} str1 First string to check.78* @param {string} str2 Second string to check.79* @return {boolean} True if `str1` and `str2` are the same string,80* ignoring case.81* @see goog.string.caseInsensitiveEquals82*/83goog.string.internal.caseInsensitiveEquals = function(str1, str2) {84'use strict';85return str1.toLowerCase() == str2.toLowerCase();86};878889/**90* Checks if a string is empty or contains only whitespaces.91* @param {string} str The string to check.92* @return {boolean} Whether `str` is empty or whitespace only.93* @see goog.string.isEmptyOrWhitespace94*/95goog.string.internal.isEmptyOrWhitespace = function(str) {96'use strict';97// testing length == 0 first is actually slower in all browsers (about the98// same in Opera).99// Since IE doesn't include non-breaking-space (0xa0) in their \s character100// class (as required by section 7.2 of the ECMAScript spec), we explicitly101// include it in the regexp to enforce consistent cross-browser behavior.102return /^[\s\xa0]*$/.test(str);103};104105106/**107* Trims white spaces to the left and right of a string.108* @param {string} str The string to trim.109* @return {string} A trimmed copy of `str`.110*/111goog.string.internal.trim =112(goog.TRUSTED_SITE && String.prototype.trim) ? function(str) {113'use strict';114return str.trim();115} : function(str) {116'use strict';117// Since IE doesn't include non-breaking-space (0xa0) in their \s118// character class (as required by section 7.2 of the ECMAScript spec),119// we explicitly include it in the regexp to enforce consistent120// cross-browser behavior.121// NOTE: We don't use String#replace because it might have side effects122// causing this function to not compile to 0 bytes.123return /^[\s\xa0]*([\s\S]*?)[\s\xa0]*$/.exec(str)[1];124};125126127/**128* A string comparator that ignores case.129* -1 = str1 less than str2130* 0 = str1 equals str2131* 1 = str1 greater than str2132*133* @param {string} str1 The string to compare.134* @param {string} str2 The string to compare `str1` to.135* @return {number} The comparator result, as described above.136* @see goog.string.caseInsensitiveCompare137*/138goog.string.internal.caseInsensitiveCompare = function(str1, str2) {139'use strict';140const test1 = String(str1).toLowerCase();141const test2 = String(str2).toLowerCase();142143if (test1 < test2) {144return -1;145} else if (test1 == test2) {146return 0;147} else {148return 1;149}150};151152153/**154* Converts \n to <br>s or <br />s.155* @param {string} str The string in which to convert newlines.156* @param {boolean=} opt_xml Whether to use XML compatible tags.157* @return {string} A copy of `str` with converted newlines.158* @see goog.string.newLineToBr159*/160goog.string.internal.newLineToBr = function(str, opt_xml) {161'use strict';162return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');163};164165166/**167* Escapes double quote '"' and single quote '\'' characters in addition to168* '&', '<', and '>' so that a string can be included in an HTML tag attribute169* value within double or single quotes.170* @param {string} str string to be escaped.171* @param {boolean=} opt_isLikelyToContainHtmlChars172* @return {string} An escaped copy of `str`.173* @see goog.string.htmlEscape174*/175goog.string.internal.htmlEscape = function(176str, opt_isLikelyToContainHtmlChars) {177'use strict';178if (opt_isLikelyToContainHtmlChars) {179str = str.replace(goog.string.internal.AMP_RE_, '&')180.replace(goog.string.internal.LT_RE_, '<')181.replace(goog.string.internal.GT_RE_, '>')182.replace(goog.string.internal.QUOT_RE_, '"')183.replace(goog.string.internal.SINGLE_QUOTE_RE_, ''')184.replace(goog.string.internal.NULL_RE_, '�');185return str;186187} else {188// quick test helps in the case when there are no chars to replace, in189// worst case this makes barely a difference to the time taken190if (!goog.string.internal.ALL_RE_.test(str)) return str;191192// str.indexOf is faster than regex.test in this case193if (str.indexOf('&') != -1) {194str = str.replace(goog.string.internal.AMP_RE_, '&');195}196if (str.indexOf('<') != -1) {197str = str.replace(goog.string.internal.LT_RE_, '<');198}199if (str.indexOf('>') != -1) {200str = str.replace(goog.string.internal.GT_RE_, '>');201}202if (str.indexOf('"') != -1) {203str = str.replace(goog.string.internal.QUOT_RE_, '"');204}205if (str.indexOf('\'') != -1) {206str = str.replace(goog.string.internal.SINGLE_QUOTE_RE_, ''');207}208if (str.indexOf('\x00') != -1) {209str = str.replace(goog.string.internal.NULL_RE_, '�');210}211return str;212}213};214215216/**217* Regular expression that matches an ampersand, for use in escaping.218* @const {!RegExp}219* @private220*/221goog.string.internal.AMP_RE_ = /&/g;222223224/**225* Regular expression that matches a less than sign, for use in escaping.226* @const {!RegExp}227* @private228*/229goog.string.internal.LT_RE_ = /</g;230231232/**233* Regular expression that matches a greater than sign, for use in escaping.234* @const {!RegExp}235* @private236*/237goog.string.internal.GT_RE_ = />/g;238239240/**241* Regular expression that matches a double quote, for use in escaping.242* @const {!RegExp}243* @private244*/245goog.string.internal.QUOT_RE_ = /"/g;246247248/**249* Regular expression that matches a single quote, for use in escaping.250* @const {!RegExp}251* @private252*/253goog.string.internal.SINGLE_QUOTE_RE_ = /'/g;254255256/**257* Regular expression that matches null character, for use in escaping.258* @const {!RegExp}259* @private260*/261goog.string.internal.NULL_RE_ = /\x00/g;262263264/**265* Regular expression that matches any character that needs to be escaped.266* @const {!RegExp}267* @private268*/269goog.string.internal.ALL_RE_ = /[\x00&<>"']/;270271272/**273* Do escaping of whitespace to preserve spatial formatting. We use character274* entity #160 to make it safer for xml.275* @param {string} str The string in which to escape whitespace.276* @param {boolean=} opt_xml Whether to use XML compatible tags.277* @return {string} An escaped copy of `str`.278* @see goog.string.whitespaceEscape279*/280goog.string.internal.whitespaceEscape = function(str, opt_xml) {281'use strict';282// This doesn't use goog.string.preserveSpaces for backwards compatibility.283return goog.string.internal.newLineToBr(284str.replace(/ /g, '  '), opt_xml);285};286287288/**289* Determines whether a string contains a substring.290* @param {string} str The string to search.291* @param {string} subString The substring to search for.292* @return {boolean} Whether `str` contains `subString`.293* @see goog.string.contains294*/295goog.string.internal.contains = function(str, subString) {296'use strict';297return str.indexOf(subString) != -1;298};299300301/**302* Determines whether a string contains a substring, ignoring case.303* @param {string} str The string to search.304* @param {string} subString The substring to search for.305* @return {boolean} Whether `str` contains `subString`.306* @see goog.string.caseInsensitiveContains307*/308goog.string.internal.caseInsensitiveContains = function(str, subString) {309'use strict';310return goog.string.internal.contains(311str.toLowerCase(), subString.toLowerCase());312};313314315/**316* Compares two version numbers.317*318* @param {string|number} version1 Version of first item.319* @param {string|number} version2 Version of second item.320*321* @return {number} 1 if `version1` is higher.322* 0 if arguments are equal.323* -1 if `version2` is higher.324* @see goog.string.compareVersions325*/326goog.string.internal.compareVersions = function(version1, version2) {327'use strict';328let order = 0;329// Trim leading and trailing whitespace and split the versions into330// subversions.331const v1Subs = goog.string.internal.trim(String(version1)).split('.');332const v2Subs = goog.string.internal.trim(String(version2)).split('.');333const subCount = Math.max(v1Subs.length, v2Subs.length);334335// Iterate over the subversions, as long as they appear to be equivalent.336for (let subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {337let v1Sub = v1Subs[subIdx] || '';338let v2Sub = v2Subs[subIdx] || '';339340do {341// Split the subversions into pairs of numbers and qualifiers (like 'b').342// Two different RegExp objects are use to make it clear the code343// is side-effect free344const v1Comp = /(\d*)(\D*)(.*)/.exec(v1Sub) || ['', '', '', ''];345const v2Comp = /(\d*)(\D*)(.*)/.exec(v2Sub) || ['', '', '', ''];346// Break if there are no more matches.347if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {348break;349}350351// Parse the numeric part of the subversion. A missing number is352// equivalent to 0.353const v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);354const v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);355356// Compare the subversion components. The number has the highest357// precedence. Next, if the numbers are equal, a subversion without any358// qualifier is always higher than a subversion with any qualifier. Next,359// the qualifiers are compared as strings.360order = goog.string.internal.compareElements_(v1CompNum, v2CompNum) ||361goog.string.internal.compareElements_(362v1Comp[2].length == 0, v2Comp[2].length == 0) ||363goog.string.internal.compareElements_(v1Comp[2], v2Comp[2]);364// Stop as soon as an inequality is discovered.365366v1Sub = v1Comp[3];367v2Sub = v2Comp[3];368} while (order == 0);369}370371return order;372};373374375/**376* Compares elements of a version number.377*378* @param {string|number|boolean} left An element from a version number.379* @param {string|number|boolean} right An element from a version number.380*381* @return {number} 1 if `left` is higher.382* 0 if arguments are equal.383* -1 if `right` is higher.384* @private385*/386goog.string.internal.compareElements_ = function(left, right) {387'use strict';388if (left < right) {389return -1;390} else if (left > right) {391return 1;392}393return 0;394};395396397