lib/goog/string/string.js

1// Copyright 2006 The Closure Library Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS-IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/**
16 * @fileoverview Utilities for string manipulation.
17 */
18
19
20/**
21 * Namespace for string utilities
22 */
23goog.provide('goog.string');
24goog.provide('goog.string.Unicode');
25
26
27/**
28 * @define {boolean} Enables HTML escaping of lowercase letter "e" which helps
29 * with detection of double-escaping as this letter is frequently used.
30 */
31goog.define('goog.string.DETECT_DOUBLE_ESCAPING', false);
32
33
34/**
35 * Common Unicode string characters.
36 * @enum {string}
37 */
38goog.string.Unicode = {
39 NBSP: '\xa0'
40};
41
42
43/**
44 * Fast prefix-checker.
45 * @param {string} str The string to check.
46 * @param {string} prefix A string to look for at the start of {@code str}.
47 * @return {boolean} True if {@code str} begins with {@code prefix}.
48 */
49goog.string.startsWith = function(str, prefix) {
50 return str.lastIndexOf(prefix, 0) == 0;
51};
52
53
54/**
55 * Fast suffix-checker.
56 * @param {string} str The string to check.
57 * @param {string} suffix A string to look for at the end of {@code str}.
58 * @return {boolean} True if {@code str} ends with {@code suffix}.
59 */
60goog.string.endsWith = function(str, suffix) {
61 var l = str.length - suffix.length;
62 return l >= 0 && str.indexOf(suffix, l) == l;
63};
64
65
66/**
67 * Case-insensitive prefix-checker.
68 * @param {string} str The string to check.
69 * @param {string} prefix A string to look for at the end of {@code str}.
70 * @return {boolean} True if {@code str} begins with {@code prefix} (ignoring
71 * case).
72 */
73goog.string.caseInsensitiveStartsWith = function(str, prefix) {
74 return goog.string.caseInsensitiveCompare(
75 prefix, str.substr(0, prefix.length)) == 0;
76};
77
78
79/**
80 * Case-insensitive suffix-checker.
81 * @param {string} str The string to check.
82 * @param {string} suffix A string to look for at the end of {@code str}.
83 * @return {boolean} True if {@code str} ends with {@code suffix} (ignoring
84 * case).
85 */
86goog.string.caseInsensitiveEndsWith = function(str, suffix) {
87 return goog.string.caseInsensitiveCompare(
88 suffix, str.substr(str.length - suffix.length, suffix.length)) == 0;
89};
90
91
92/**
93 * Case-insensitive equality checker.
94 * @param {string} str1 First string to check.
95 * @param {string} str2 Second string to check.
96 * @return {boolean} True if {@code str1} and {@code str2} are the same string,
97 * ignoring case.
98 */
99goog.string.caseInsensitiveEquals = function(str1, str2) {
100 return str1.toLowerCase() == str2.toLowerCase();
101};
102
103
104/**
105 * Does simple python-style string substitution.
106 * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog".
107 * @param {string} str The string containing the pattern.
108 * @param {...*} var_args The items to substitute into the pattern.
109 * @return {string} A copy of {@code str} in which each occurrence of
110 * {@code %s} has been replaced an argument from {@code var_args}.
111 */
112goog.string.subs = function(str, var_args) {
113 var splitParts = str.split('%s');
114 var returnString = '';
115
116 var subsArguments = Array.prototype.slice.call(arguments, 1);
117 while (subsArguments.length &&
118 // Replace up to the last split part. We are inserting in the
119 // positions between split parts.
120 splitParts.length > 1) {
121 returnString += splitParts.shift() + subsArguments.shift();
122 }
123
124 return returnString + splitParts.join('%s'); // Join unused '%s'
125};
126
127
128/**
129 * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines
130 * and tabs) to a single space, and strips leading and trailing whitespace.
131 * @param {string} str Input string.
132 * @return {string} A copy of {@code str} with collapsed whitespace.
133 */
134goog.string.collapseWhitespace = function(str) {
135 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
136 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
137 // include it in the regexp to enforce consistent cross-browser behavior.
138 return str.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, '');
139};
140
141
142/**
143 * Checks if a string is empty or contains only whitespaces.
144 * @param {string} str The string to check.
145 * @return {boolean} True if {@code str} is empty or whitespace only.
146 */
147goog.string.isEmpty = function(str) {
148 // testing length == 0 first is actually slower in all browsers (about the
149 // same in Opera).
150 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
151 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
152 // include it in the regexp to enforce consistent cross-browser behavior.
153 return /^[\s\xa0]*$/.test(str);
154};
155
156
157/**
158 * Checks if a string is null, undefined, empty or contains only whitespaces.
159 * @param {*} str The string to check.
160 * @return {boolean} True if{@code str} is null, undefined, empty, or
161 * whitespace only.
162 */
163goog.string.isEmptySafe = function(str) {
164 return goog.string.isEmpty(goog.string.makeSafe(str));
165};
166
167
168/**
169 * Checks if a string is all breaking whitespace.
170 * @param {string} str The string to check.
171 * @return {boolean} Whether the string is all breaking whitespace.
172 */
173goog.string.isBreakingWhitespace = function(str) {
174 return !/[^\t\n\r ]/.test(str);
175};
176
177
178/**
179 * Checks if a string contains all letters.
180 * @param {string} str string to check.
181 * @return {boolean} True if {@code str} consists entirely of letters.
182 */
183goog.string.isAlpha = function(str) {
184 return !/[^a-zA-Z]/.test(str);
185};
186
187
188/**
189 * Checks if a string contains only numbers.
190 * @param {*} str string to check. If not a string, it will be
191 * casted to one.
192 * @return {boolean} True if {@code str} is numeric.
193 */
194goog.string.isNumeric = function(str) {
195 return !/[^0-9]/.test(str);
196};
197
198
199/**
200 * Checks if a string contains only numbers or letters.
201 * @param {string} str string to check.
202 * @return {boolean} True if {@code str} is alphanumeric.
203 */
204goog.string.isAlphaNumeric = function(str) {
205 return !/[^a-zA-Z0-9]/.test(str);
206};
207
208
209/**
210 * Checks if a character is a space character.
211 * @param {string} ch Character to check.
212 * @return {boolean} True if {code ch} is a space.
213 */
214goog.string.isSpace = function(ch) {
215 return ch == ' ';
216};
217
218
219/**
220 * Checks if a character is a valid unicode character.
221 * @param {string} ch Character to check.
222 * @return {boolean} True if {code ch} is a valid unicode character.
223 */
224goog.string.isUnicodeChar = function(ch) {
225 return ch.length == 1 && ch >= ' ' && ch <= '~' ||
226 ch >= '\u0080' && ch <= '\uFFFD';
227};
228
229
230/**
231 * Takes a string and replaces newlines with a space. Multiple lines are
232 * replaced with a single space.
233 * @param {string} str The string from which to strip newlines.
234 * @return {string} A copy of {@code str} stripped of newlines.
235 */
236goog.string.stripNewlines = function(str) {
237 return str.replace(/(\r\n|\r|\n)+/g, ' ');
238};
239
240
241/**
242 * Replaces Windows and Mac new lines with unix style: \r or \r\n with \n.
243 * @param {string} str The string to in which to canonicalize newlines.
244 * @return {string} {@code str} A copy of {@code} with canonicalized newlines.
245 */
246goog.string.canonicalizeNewlines = function(str) {
247 return str.replace(/(\r\n|\r|\n)/g, '\n');
248};
249
250
251/**
252 * Normalizes whitespace in a string, replacing all whitespace chars with
253 * a space.
254 * @param {string} str The string in which to normalize whitespace.
255 * @return {string} A copy of {@code str} with all whitespace normalized.
256 */
257goog.string.normalizeWhitespace = function(str) {
258 return str.replace(/\xa0|\s/g, ' ');
259};
260
261
262/**
263 * Normalizes spaces in a string, replacing all consecutive spaces and tabs
264 * with a single space. Replaces non-breaking space with a space.
265 * @param {string} str The string in which to normalize spaces.
266 * @return {string} A copy of {@code str} with all consecutive spaces and tabs
267 * replaced with a single space.
268 */
269goog.string.normalizeSpaces = function(str) {
270 return str.replace(/\xa0|[ \t]+/g, ' ');
271};
272
273
274/**
275 * Removes the breaking spaces from the left and right of the string and
276 * collapses the sequences of breaking spaces in the middle into single spaces.
277 * The original and the result strings render the same way in HTML.
278 * @param {string} str A string in which to collapse spaces.
279 * @return {string} Copy of the string with normalized breaking spaces.
280 */
281goog.string.collapseBreakingSpaces = function(str) {
282 return str.replace(/[\t\r\n ]+/g, ' ').replace(
283 /^[\t\r\n ]+|[\t\r\n ]+$/g, '');
284};
285
286
287/**
288 * Trims white spaces to the left and right of a string.
289 * @param {string} str The string to trim.
290 * @return {string} A trimmed copy of {@code str}.
291 */
292goog.string.trim = function(str) {
293 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
294 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
295 // include it in the regexp to enforce consistent cross-browser behavior.
296 return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
297};
298
299
300/**
301 * Trims whitespaces at the left end of a string.
302 * @param {string} str The string to left trim.
303 * @return {string} A trimmed copy of {@code str}.
304 */
305goog.string.trimLeft = function(str) {
306 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
307 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
308 // include it in the regexp to enforce consistent cross-browser behavior.
309 return str.replace(/^[\s\xa0]+/, '');
310};
311
312
313/**
314 * Trims whitespaces at the right end of a string.
315 * @param {string} str The string to right trim.
316 * @return {string} A trimmed copy of {@code str}.
317 */
318goog.string.trimRight = function(str) {
319 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
320 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
321 // include it in the regexp to enforce consistent cross-browser behavior.
322 return str.replace(/[\s\xa0]+$/, '');
323};
324
325
326/**
327 * A string comparator that ignores case.
328 * -1 = str1 less than str2
329 * 0 = str1 equals str2
330 * 1 = str1 greater than str2
331 *
332 * @param {string} str1 The string to compare.
333 * @param {string} str2 The string to compare {@code str1} to.
334 * @return {number} The comparator result, as described above.
335 */
336goog.string.caseInsensitiveCompare = function(str1, str2) {
337 var test1 = String(str1).toLowerCase();
338 var test2 = String(str2).toLowerCase();
339
340 if (test1 < test2) {
341 return -1;
342 } else if (test1 == test2) {
343 return 0;
344 } else {
345 return 1;
346 }
347};
348
349
350/**
351 * Regular expression used for splitting a string into substrings of fractional
352 * numbers, integers, and non-numeric characters.
353 * @type {RegExp}
354 * @private
355 */
356goog.string.numerateCompareRegExp_ = /(\.\d+)|(\d+)|(\D+)/g;
357
358
359/**
360 * String comparison function that handles numbers in a way humans might expect.
361 * Using this function, the string "File 2.jpg" sorts before "File 10.jpg". The
362 * comparison is mostly case-insensitive, though strings that are identical
363 * except for case are sorted with the upper-case strings before lower-case.
364 *
365 * This comparison function is significantly slower (about 500x) than either
366 * the default or the case-insensitive compare. It should not be used in
367 * time-critical code, but should be fast enough to sort several hundred short
368 * strings (like filenames) with a reasonable delay.
369 *
370 * @param {string} str1 The string to compare in a numerically sensitive way.
371 * @param {string} str2 The string to compare {@code str1} to.
372 * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
373 * 0 if str1 > str2.
374 */
375goog.string.numerateCompare = function(str1, str2) {
376 if (str1 == str2) {
377 return 0;
378 }
379 if (!str1) {
380 return -1;
381 }
382 if (!str2) {
383 return 1;
384 }
385
386 // Using match to split the entire string ahead of time turns out to be faster
387 // for most inputs than using RegExp.exec or iterating over each character.
388 var tokens1 = str1.toLowerCase().match(goog.string.numerateCompareRegExp_);
389 var tokens2 = str2.toLowerCase().match(goog.string.numerateCompareRegExp_);
390
391 var count = Math.min(tokens1.length, tokens2.length);
392
393 for (var i = 0; i < count; i++) {
394 var a = tokens1[i];
395 var b = tokens2[i];
396
397 // Compare pairs of tokens, returning if one token sorts before the other.
398 if (a != b) {
399
400 // Only if both tokens are integers is a special comparison required.
401 // Decimal numbers are sorted as strings (e.g., '.09' < '.1').
402 var num1 = parseInt(a, 10);
403 if (!isNaN(num1)) {
404 var num2 = parseInt(b, 10);
405 if (!isNaN(num2) && num1 - num2) {
406 return num1 - num2;
407 }
408 }
409 return a < b ? -1 : 1;
410 }
411 }
412
413 // If one string is a substring of the other, the shorter string sorts first.
414 if (tokens1.length != tokens2.length) {
415 return tokens1.length - tokens2.length;
416 }
417
418 // The two strings must be equivalent except for case (perfect equality is
419 // tested at the head of the function.) Revert to default ASCII-betical string
420 // comparison to stablize the sort.
421 return str1 < str2 ? -1 : 1;
422};
423
424
425/**
426 * URL-encodes a string
427 * @param {*} str The string to url-encode.
428 * @return {string} An encoded copy of {@code str} that is safe for urls.
429 * Note that '#', ':', and other characters used to delimit portions
430 * of URLs *will* be encoded.
431 */
432goog.string.urlEncode = function(str) {
433 return encodeURIComponent(String(str));
434};
435
436
437/**
438 * URL-decodes the string. We need to specially handle '+'s because
439 * the javascript library doesn't convert them to spaces.
440 * @param {string} str The string to url decode.
441 * @return {string} The decoded {@code str}.
442 */
443goog.string.urlDecode = function(str) {
444 return decodeURIComponent(str.replace(/\+/g, ' '));
445};
446
447
448/**
449 * Converts \n to <br>s or <br />s.
450 * @param {string} str The string in which to convert newlines.
451 * @param {boolean=} opt_xml Whether to use XML compatible tags.
452 * @return {string} A copy of {@code str} with converted newlines.
453 */
454goog.string.newLineToBr = function(str, opt_xml) {
455 return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');
456};
457
458
459/**
460 * Escapes double quote '"' and single quote '\'' characters in addition to
461 * '&', '<', and '>' so that a string can be included in an HTML tag attribute
462 * value within double or single quotes.
463 *
464 * It should be noted that > doesn't need to be escaped for the HTML or XML to
465 * be valid, but it has been decided to escape it for consistency with other
466 * implementations.
467 *
468 * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the
469 * lowercase letter "e".
470 *
471 * NOTE(user):
472 * HtmlEscape is often called during the generation of large blocks of HTML.
473 * Using statics for the regular expressions and strings is an optimization
474 * that can more than half the amount of time IE spends in this function for
475 * large apps, since strings and regexes both contribute to GC allocations.
476 *
477 * Testing for the presence of a character before escaping increases the number
478 * of function calls, but actually provides a speed increase for the average
479 * case -- since the average case often doesn't require the escaping of all 4
480 * characters and indexOf() is much cheaper than replace().
481 * The worst case does suffer slightly from the additional calls, therefore the
482 * opt_isLikelyToContainHtmlChars option has been included for situations
483 * where all 4 HTML entities are very likely to be present and need escaping.
484 *
485 * Some benchmarks (times tended to fluctuate +-0.05ms):
486 * FireFox IE6
487 * (no chars / average (mix of cases) / all 4 chars)
488 * no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80
489 * indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84
490 * indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85
491 *
492 * An additional advantage of checking if replace actually needs to be called
493 * is a reduction in the number of object allocations, so as the size of the
494 * application grows the difference between the various methods would increase.
495 *
496 * @param {string} str string to be escaped.
497 * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see
498 * if the character needs replacing - use this option if you expect each of
499 * the characters to appear often. Leave false if you expect few html
500 * characters to occur in your strings, such as if you are escaping HTML.
501 * @return {string} An escaped copy of {@code str}.
502 */
503goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {
504
505 if (opt_isLikelyToContainHtmlChars) {
506 str = str.replace(goog.string.AMP_RE_, '&amp;')
507 .replace(goog.string.LT_RE_, '&lt;')
508 .replace(goog.string.GT_RE_, '&gt;')
509 .replace(goog.string.QUOT_RE_, '&quot;')
510 .replace(goog.string.SINGLE_QUOTE_RE_, '&#39;')
511 .replace(goog.string.NULL_RE_, '&#0;');
512 if (goog.string.DETECT_DOUBLE_ESCAPING) {
513 str = str.replace(goog.string.E_RE_, '&#101;');
514 }
515 return str;
516
517 } else {
518 // quick test helps in the case when there are no chars to replace, in
519 // worst case this makes barely a difference to the time taken
520 if (!goog.string.ALL_RE_.test(str)) return str;
521
522 // str.indexOf is faster than regex.test in this case
523 if (str.indexOf('&') != -1) {
524 str = str.replace(goog.string.AMP_RE_, '&amp;');
525 }
526 if (str.indexOf('<') != -1) {
527 str = str.replace(goog.string.LT_RE_, '&lt;');
528 }
529 if (str.indexOf('>') != -1) {
530 str = str.replace(goog.string.GT_RE_, '&gt;');
531 }
532 if (str.indexOf('"') != -1) {
533 str = str.replace(goog.string.QUOT_RE_, '&quot;');
534 }
535 if (str.indexOf('\'') != -1) {
536 str = str.replace(goog.string.SINGLE_QUOTE_RE_, '&#39;');
537 }
538 if (str.indexOf('\x00') != -1) {
539 str = str.replace(goog.string.NULL_RE_, '&#0;');
540 }
541 if (goog.string.DETECT_DOUBLE_ESCAPING && str.indexOf('e') != -1) {
542 str = str.replace(goog.string.E_RE_, '&#101;');
543 }
544 return str;
545 }
546};
547
548
549/**
550 * Regular expression that matches an ampersand, for use in escaping.
551 * @const {!RegExp}
552 * @private
553 */
554goog.string.AMP_RE_ = /&/g;
555
556
557/**
558 * Regular expression that matches a less than sign, for use in escaping.
559 * @const {!RegExp}
560 * @private
561 */
562goog.string.LT_RE_ = /</g;
563
564
565/**
566 * Regular expression that matches a greater than sign, for use in escaping.
567 * @const {!RegExp}
568 * @private
569 */
570goog.string.GT_RE_ = />/g;
571
572
573/**
574 * Regular expression that matches a double quote, for use in escaping.
575 * @const {!RegExp}
576 * @private
577 */
578goog.string.QUOT_RE_ = /"/g;
579
580
581/**
582 * Regular expression that matches a single quote, for use in escaping.
583 * @const {!RegExp}
584 * @private
585 */
586goog.string.SINGLE_QUOTE_RE_ = /'/g;
587
588
589/**
590 * Regular expression that matches null character, for use in escaping.
591 * @const {!RegExp}
592 * @private
593 */
594goog.string.NULL_RE_ = /\x00/g;
595
596
597/**
598 * Regular expression that matches a lowercase letter "e", for use in escaping.
599 * @const {!RegExp}
600 * @private
601 */
602goog.string.E_RE_ = /e/g;
603
604
605/**
606 * Regular expression that matches any character that needs to be escaped.
607 * @const {!RegExp}
608 * @private
609 */
610goog.string.ALL_RE_ = (goog.string.DETECT_DOUBLE_ESCAPING ?
611 /[\x00&<>"'e]/ :
612 /[\x00&<>"']/);
613
614
615/**
616 * Unescapes an HTML string.
617 *
618 * @param {string} str The string to unescape.
619 * @return {string} An unescaped copy of {@code str}.
620 */
621goog.string.unescapeEntities = function(str) {
622 if (goog.string.contains(str, '&')) {
623 // We are careful not to use a DOM if we do not have one. We use the []
624 // notation so that the JSCompiler will not complain about these objects and
625 // fields in the case where we have no DOM.
626 if ('document' in goog.global) {
627 return goog.string.unescapeEntitiesUsingDom_(str);
628 } else {
629 // Fall back on pure XML entities
630 return goog.string.unescapePureXmlEntities_(str);
631 }
632 }
633 return str;
634};
635
636
637/**
638 * Unescapes a HTML string using the provided document.
639 *
640 * @param {string} str The string to unescape.
641 * @param {!Document} document A document to use in escaping the string.
642 * @return {string} An unescaped copy of {@code str}.
643 */
644goog.string.unescapeEntitiesWithDocument = function(str, document) {
645 if (goog.string.contains(str, '&')) {
646 return goog.string.unescapeEntitiesUsingDom_(str, document);
647 }
648 return str;
649};
650
651
652/**
653 * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric
654 * entities. This function is XSS-safe and whitespace-preserving.
655 * @private
656 * @param {string} str The string to unescape.
657 * @param {Document=} opt_document An optional document to use for creating
658 * elements. If this is not specified then the default window.document
659 * will be used.
660 * @return {string} The unescaped {@code str} string.
661 */
662goog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) {
663 var seen = {'&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"'};
664 var div;
665 if (opt_document) {
666 div = opt_document.createElement('div');
667 } else {
668 div = goog.global.document.createElement('div');
669 }
670 // Match as many valid entity characters as possible. If the actual entity
671 // happens to be shorter, it will still work as innerHTML will return the
672 // trailing characters unchanged. Since the entity characters do not include
673 // open angle bracket, there is no chance of XSS from the innerHTML use.
674 // Since no whitespace is passed to innerHTML, whitespace is preserved.
675 return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {
676 // Check for cached entity.
677 var value = seen[s];
678 if (value) {
679 return value;
680 }
681 // Check for numeric entity.
682 if (entity.charAt(0) == '#') {
683 // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex numbers.
684 var n = Number('0' + entity.substr(1));
685 if (!isNaN(n)) {
686 value = String.fromCharCode(n);
687 }
688 }
689 // Fall back to innerHTML otherwise.
690 if (!value) {
691 // Append a non-entity character to avoid a bug in Webkit that parses
692 // an invalid entity at the end of innerHTML text as the empty string.
693 div.innerHTML = s + ' ';
694 // Then remove the trailing character from the result.
695 value = div.firstChild.nodeValue.slice(0, -1);
696 }
697 // Cache and return.
698 return seen[s] = value;
699 });
700};
701
702
703/**
704 * Unescapes XML entities.
705 * @private
706 * @param {string} str The string to unescape.
707 * @return {string} An unescaped copy of {@code str}.
708 */
709goog.string.unescapePureXmlEntities_ = function(str) {
710 return str.replace(/&([^;]+);/g, function(s, entity) {
711 switch (entity) {
712 case 'amp':
713 return '&';
714 case 'lt':
715 return '<';
716 case 'gt':
717 return '>';
718 case 'quot':
719 return '"';
720 default:
721 if (entity.charAt(0) == '#') {
722 // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex.
723 var n = Number('0' + entity.substr(1));
724 if (!isNaN(n)) {
725 return String.fromCharCode(n);
726 }
727 }
728 // For invalid entities we just return the entity
729 return s;
730 }
731 });
732};
733
734
735/**
736 * Regular expression that matches an HTML entity.
737 * See also HTML5: Tokenization / Tokenizing character references.
738 * @private
739 * @type {!RegExp}
740 */
741goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g;
742
743
744/**
745 * Do escaping of whitespace to preserve spatial formatting. We use character
746 * entity #160 to make it safer for xml.
747 * @param {string} str The string in which to escape whitespace.
748 * @param {boolean=} opt_xml Whether to use XML compatible tags.
749 * @return {string} An escaped copy of {@code str}.
750 */
751goog.string.whitespaceEscape = function(str, opt_xml) {
752 // This doesn't use goog.string.preserveSpaces for backwards compatibility.
753 return goog.string.newLineToBr(str.replace(/ /g, ' &#160;'), opt_xml);
754};
755
756
757/**
758 * Preserve spaces that would be otherwise collapsed in HTML by replacing them
759 * with non-breaking space Unicode characters.
760 * @param {string} str The string in which to preserve whitespace.
761 * @return {string} A copy of {@code str} with preserved whitespace.
762 */
763goog.string.preserveSpaces = function(str) {
764 return str.replace(/(^|[\n ]) /g, '$1' + goog.string.Unicode.NBSP);
765};
766
767
768/**
769 * Strip quote characters around a string. The second argument is a string of
770 * characters to treat as quotes. This can be a single character or a string of
771 * multiple character and in that case each of those are treated as possible
772 * quote characters. For example:
773 *
774 * <pre>
775 * goog.string.stripQuotes('"abc"', '"`') --> 'abc'
776 * goog.string.stripQuotes('`abc`', '"`') --> 'abc'
777 * </pre>
778 *
779 * @param {string} str The string to strip.
780 * @param {string} quoteChars The quote characters to strip.
781 * @return {string} A copy of {@code str} without the quotes.
782 */
783goog.string.stripQuotes = function(str, quoteChars) {
784 var length = quoteChars.length;
785 for (var i = 0; i < length; i++) {
786 var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i);
787 if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) {
788 return str.substring(1, str.length - 1);
789 }
790 }
791 return str;
792};
793
794
795/**
796 * Truncates a string to a certain length and adds '...' if necessary. The
797 * length also accounts for the ellipsis, so a maximum length of 10 and a string
798 * 'Hello World!' produces 'Hello W...'.
799 * @param {string} str The string to truncate.
800 * @param {number} chars Max number of characters.
801 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
802 * characters from being cut off in the middle.
803 * @return {string} The truncated {@code str} string.
804 */
805goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) {
806 if (opt_protectEscapedCharacters) {
807 str = goog.string.unescapeEntities(str);
808 }
809
810 if (str.length > chars) {
811 str = str.substring(0, chars - 3) + '...';
812 }
813
814 if (opt_protectEscapedCharacters) {
815 str = goog.string.htmlEscape(str);
816 }
817
818 return str;
819};
820
821
822/**
823 * Truncate a string in the middle, adding "..." if necessary,
824 * and favoring the beginning of the string.
825 * @param {string} str The string to truncate the middle of.
826 * @param {number} chars Max number of characters.
827 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
828 * characters from being cutoff in the middle.
829 * @param {number=} opt_trailingChars Optional number of trailing characters to
830 * leave at the end of the string, instead of truncating as close to the
831 * middle as possible.
832 * @return {string} A truncated copy of {@code str}.
833 */
834goog.string.truncateMiddle = function(str, chars,
835 opt_protectEscapedCharacters, opt_trailingChars) {
836 if (opt_protectEscapedCharacters) {
837 str = goog.string.unescapeEntities(str);
838 }
839
840 if (opt_trailingChars && str.length > chars) {
841 if (opt_trailingChars > chars) {
842 opt_trailingChars = chars;
843 }
844 var endPoint = str.length - opt_trailingChars;
845 var startPoint = chars - opt_trailingChars;
846 str = str.substring(0, startPoint) + '...' + str.substring(endPoint);
847 } else if (str.length > chars) {
848 // Favor the beginning of the string:
849 var half = Math.floor(chars / 2);
850 var endPos = str.length - half;
851 half += chars % 2;
852 str = str.substring(0, half) + '...' + str.substring(endPos);
853 }
854
855 if (opt_protectEscapedCharacters) {
856 str = goog.string.htmlEscape(str);
857 }
858
859 return str;
860};
861
862
863/**
864 * Special chars that need to be escaped for goog.string.quote.
865 * @private
866 * @type {Object}
867 */
868goog.string.specialEscapeChars_ = {
869 '\0': '\\0',
870 '\b': '\\b',
871 '\f': '\\f',
872 '\n': '\\n',
873 '\r': '\\r',
874 '\t': '\\t',
875 '\x0B': '\\x0B', // '\v' is not supported in JScript
876 '"': '\\"',
877 '\\': '\\\\'
878};
879
880
881/**
882 * Character mappings used internally for goog.string.escapeChar.
883 * @private
884 * @type {Object}
885 */
886goog.string.jsEscapeCache_ = {
887 '\'': '\\\''
888};
889
890
891/**
892 * Encloses a string in double quotes and escapes characters so that the
893 * string is a valid JS string.
894 * @param {string} s The string to quote.
895 * @return {string} A copy of {@code s} surrounded by double quotes.
896 */
897goog.string.quote = function(s) {
898 s = String(s);
899 if (s.quote) {
900 return s.quote();
901 } else {
902 var sb = ['"'];
903 for (var i = 0; i < s.length; i++) {
904 var ch = s.charAt(i);
905 var cc = ch.charCodeAt(0);
906 sb[i + 1] = goog.string.specialEscapeChars_[ch] ||
907 ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch));
908 }
909 sb.push('"');
910 return sb.join('');
911 }
912};
913
914
915/**
916 * Takes a string and returns the escaped string for that character.
917 * @param {string} str The string to escape.
918 * @return {string} An escaped string representing {@code str}.
919 */
920goog.string.escapeString = function(str) {
921 var sb = [];
922 for (var i = 0; i < str.length; i++) {
923 sb[i] = goog.string.escapeChar(str.charAt(i));
924 }
925 return sb.join('');
926};
927
928
929/**
930 * Takes a character and returns the escaped string for that character. For
931 * example escapeChar(String.fromCharCode(15)) -> "\\x0E".
932 * @param {string} c The character to escape.
933 * @return {string} An escaped string representing {@code c}.
934 */
935goog.string.escapeChar = function(c) {
936 if (c in goog.string.jsEscapeCache_) {
937 return goog.string.jsEscapeCache_[c];
938 }
939
940 if (c in goog.string.specialEscapeChars_) {
941 return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c];
942 }
943
944 var rv = c;
945 var cc = c.charCodeAt(0);
946 if (cc > 31 && cc < 127) {
947 rv = c;
948 } else {
949 // tab is 9 but handled above
950 if (cc < 256) {
951 rv = '\\x';
952 if (cc < 16 || cc > 256) {
953 rv += '0';
954 }
955 } else {
956 rv = '\\u';
957 if (cc < 4096) { // \u1000
958 rv += '0';
959 }
960 }
961 rv += cc.toString(16).toUpperCase();
962 }
963
964 return goog.string.jsEscapeCache_[c] = rv;
965};
966
967
968/**
969 * Takes a string and creates a map (Object) in which the keys are the
970 * characters in the string. The value for the key is set to true. You can
971 * then use goog.object.map or goog.array.map to change the values.
972 * @param {string} s The string to build the map from.
973 * @return {!Object} The map of characters used.
974 */
975// TODO(arv): It seems like we should have a generic goog.array.toMap. But do
976// we want a dependency on goog.array in goog.string?
977goog.string.toMap = function(s) {
978 var rv = {};
979 for (var i = 0; i < s.length; i++) {
980 rv[s.charAt(i)] = true;
981 }
982 return rv;
983};
984
985
986/**
987 * Determines whether a string contains a substring.
988 * @param {string} str The string to search.
989 * @param {string} subString The substring to search for.
990 * @return {boolean} Whether {@code str} contains {@code subString}.
991 */
992goog.string.contains = function(str, subString) {
993 return str.indexOf(subString) != -1;
994};
995
996
997/**
998 * Determines whether a string contains a substring, ignoring case.
999 * @param {string} str The string to search.
1000 * @param {string} subString The substring to search for.
1001 * @return {boolean} Whether {@code str} contains {@code subString}.
1002 */
1003goog.string.caseInsensitiveContains = function(str, subString) {
1004 return goog.string.contains(str.toLowerCase(), subString.toLowerCase());
1005};
1006
1007
1008/**
1009 * Returns the non-overlapping occurrences of ss in s.
1010 * If either s or ss evalutes to false, then returns zero.
1011 * @param {string} s The string to look in.
1012 * @param {string} ss The string to look for.
1013 * @return {number} Number of occurrences of ss in s.
1014 */
1015goog.string.countOf = function(s, ss) {
1016 return s && ss ? s.split(ss).length - 1 : 0;
1017};
1018
1019
1020/**
1021 * Removes a substring of a specified length at a specific
1022 * index in a string.
1023 * @param {string} s The base string from which to remove.
1024 * @param {number} index The index at which to remove the substring.
1025 * @param {number} stringLength The length of the substring to remove.
1026 * @return {string} A copy of {@code s} with the substring removed or the full
1027 * string if nothing is removed or the input is invalid.
1028 */
1029goog.string.removeAt = function(s, index, stringLength) {
1030 var resultStr = s;
1031 // If the index is greater or equal to 0 then remove substring
1032 if (index >= 0 && index < s.length && stringLength > 0) {
1033 resultStr = s.substr(0, index) +
1034 s.substr(index + stringLength, s.length - index - stringLength);
1035 }
1036 return resultStr;
1037};
1038
1039
1040/**
1041 * Removes the first occurrence of a substring from a string.
1042 * @param {string} s The base string from which to remove.
1043 * @param {string} ss The string to remove.
1044 * @return {string} A copy of {@code s} with {@code ss} removed or the full
1045 * string if nothing is removed.
1046 */
1047goog.string.remove = function(s, ss) {
1048 var re = new RegExp(goog.string.regExpEscape(ss), '');
1049 return s.replace(re, '');
1050};
1051
1052
1053/**
1054 * Removes all occurrences of a substring from a string.
1055 * @param {string} s The base string from which to remove.
1056 * @param {string} ss The string to remove.
1057 * @return {string} A copy of {@code s} with {@code ss} removed or the full
1058 * string if nothing is removed.
1059 */
1060goog.string.removeAll = function(s, ss) {
1061 var re = new RegExp(goog.string.regExpEscape(ss), 'g');
1062 return s.replace(re, '');
1063};
1064
1065
1066/**
1067 * Escapes characters in the string that are not safe to use in a RegExp.
1068 * @param {*} s The string to escape. If not a string, it will be casted
1069 * to one.
1070 * @return {string} A RegExp safe, escaped copy of {@code s}.
1071 */
1072goog.string.regExpEscape = function(s) {
1073 return String(s).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
1074 replace(/\x08/g, '\\x08');
1075};
1076
1077
1078/**
1079 * Repeats a string n times.
1080 * @param {string} string The string to repeat.
1081 * @param {number} length The number of times to repeat.
1082 * @return {string} A string containing {@code length} repetitions of
1083 * {@code string}.
1084 */
1085goog.string.repeat = function(string, length) {
1086 return new Array(length + 1).join(string);
1087};
1088
1089
1090/**
1091 * Pads number to given length and optionally rounds it to a given precision.
1092 * For example:
1093 * <pre>padNumber(1.25, 2, 3) -> '01.250'
1094 * padNumber(1.25, 2) -> '01.25'
1095 * padNumber(1.25, 2, 1) -> '01.3'
1096 * padNumber(1.25, 0) -> '1.25'</pre>
1097 *
1098 * @param {number} num The number to pad.
1099 * @param {number} length The desired length.
1100 * @param {number=} opt_precision The desired precision.
1101 * @return {string} {@code num} as a string with the given options.
1102 */
1103goog.string.padNumber = function(num, length, opt_precision) {
1104 var s = goog.isDef(opt_precision) ? num.toFixed(opt_precision) : String(num);
1105 var index = s.indexOf('.');
1106 if (index == -1) {
1107 index = s.length;
1108 }
1109 return goog.string.repeat('0', Math.max(0, length - index)) + s;
1110};
1111
1112
1113/**
1114 * Returns a string representation of the given object, with
1115 * null and undefined being returned as the empty string.
1116 *
1117 * @param {*} obj The object to convert.
1118 * @return {string} A string representation of the {@code obj}.
1119 */
1120goog.string.makeSafe = function(obj) {
1121 return obj == null ? '' : String(obj);
1122};
1123
1124
1125/**
1126 * Concatenates string expressions. This is useful
1127 * since some browsers are very inefficient when it comes to using plus to
1128 * concat strings. Be careful when using null and undefined here since
1129 * these will not be included in the result. If you need to represent these
1130 * be sure to cast the argument to a String first.
1131 * For example:
1132 * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd'
1133 * buildString(null, undefined) -> ''
1134 * </pre>
1135 * @param {...*} var_args A list of strings to concatenate. If not a string,
1136 * it will be casted to one.
1137 * @return {string} The concatenation of {@code var_args}.
1138 */
1139goog.string.buildString = function(var_args) {
1140 return Array.prototype.join.call(arguments, '');
1141};
1142
1143
1144/**
1145 * Returns a string with at least 64-bits of randomness.
1146 *
1147 * Doesn't trust Javascript's random function entirely. Uses a combination of
1148 * random and current timestamp, and then encodes the string in base-36 to
1149 * make it shorter.
1150 *
1151 * @return {string} A random string, e.g. sn1s7vb4gcic.
1152 */
1153goog.string.getRandomString = function() {
1154 var x = 2147483648;
1155 return Math.floor(Math.random() * x).toString(36) +
1156 Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36);
1157};
1158
1159
1160/**
1161 * Compares two version numbers.
1162 *
1163 * @param {string|number} version1 Version of first item.
1164 * @param {string|number} version2 Version of second item.
1165 *
1166 * @return {number} 1 if {@code version1} is higher.
1167 * 0 if arguments are equal.
1168 * -1 if {@code version2} is higher.
1169 */
1170goog.string.compareVersions = function(version1, version2) {
1171 var order = 0;
1172 // Trim leading and trailing whitespace and split the versions into
1173 // subversions.
1174 var v1Subs = goog.string.trim(String(version1)).split('.');
1175 var v2Subs = goog.string.trim(String(version2)).split('.');
1176 var subCount = Math.max(v1Subs.length, v2Subs.length);
1177
1178 // Iterate over the subversions, as long as they appear to be equivalent.
1179 for (var subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {
1180 var v1Sub = v1Subs[subIdx] || '';
1181 var v2Sub = v2Subs[subIdx] || '';
1182
1183 // Split the subversions into pairs of numbers and qualifiers (like 'b').
1184 // Two different RegExp objects are needed because they are both using
1185 // the 'g' flag.
1186 var v1CompParser = new RegExp('(\\d*)(\\D*)', 'g');
1187 var v2CompParser = new RegExp('(\\d*)(\\D*)', 'g');
1188 do {
1189 var v1Comp = v1CompParser.exec(v1Sub) || ['', '', ''];
1190 var v2Comp = v2CompParser.exec(v2Sub) || ['', '', ''];
1191 // Break if there are no more matches.
1192 if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {
1193 break;
1194 }
1195
1196 // Parse the numeric part of the subversion. A missing number is
1197 // equivalent to 0.
1198 var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);
1199 var v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);
1200
1201 // Compare the subversion components. The number has the highest
1202 // precedence. Next, if the numbers are equal, a subversion without any
1203 // qualifier is always higher than a subversion with any qualifier. Next,
1204 // the qualifiers are compared as strings.
1205 order = goog.string.compareElements_(v1CompNum, v2CompNum) ||
1206 goog.string.compareElements_(v1Comp[2].length == 0,
1207 v2Comp[2].length == 0) ||
1208 goog.string.compareElements_(v1Comp[2], v2Comp[2]);
1209 // Stop as soon as an inequality is discovered.
1210 } while (order == 0);
1211 }
1212
1213 return order;
1214};
1215
1216
1217/**
1218 * Compares elements of a version number.
1219 *
1220 * @param {string|number|boolean} left An element from a version number.
1221 * @param {string|number|boolean} right An element from a version number.
1222 *
1223 * @return {number} 1 if {@code left} is higher.
1224 * 0 if arguments are equal.
1225 * -1 if {@code right} is higher.
1226 * @private
1227 */
1228goog.string.compareElements_ = function(left, right) {
1229 if (left < right) {
1230 return -1;
1231 } else if (left > right) {
1232 return 1;
1233 }
1234 return 0;
1235};
1236
1237
1238/**
1239 * Maximum value of #goog.string.hashCode, exclusive. 2^32.
1240 * @type {number}
1241 * @private
1242 */
1243goog.string.HASHCODE_MAX_ = 0x100000000;
1244
1245
1246/**
1247 * String hash function similar to java.lang.String.hashCode().
1248 * The hash code for a string is computed as
1249 * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
1250 * where s[i] is the ith character of the string and n is the length of
1251 * the string. We mod the result to make it between 0 (inclusive) and 2^32
1252 * (exclusive).
1253 * @param {string} str A string.
1254 * @return {number} Hash value for {@code str}, between 0 (inclusive) and 2^32
1255 * (exclusive). The empty string returns 0.
1256 */
1257goog.string.hashCode = function(str) {
1258 var result = 0;
1259 for (var i = 0; i < str.length; ++i) {
1260 result = 31 * result + str.charCodeAt(i);
1261 // Normalize to 4 byte range, 0 ... 2^32.
1262 result %= goog.string.HASHCODE_MAX_;
1263 }
1264 return result;
1265};
1266
1267
1268/**
1269 * The most recent unique ID. |0 is equivalent to Math.floor in this case.
1270 * @type {number}
1271 * @private
1272 */
1273goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0;
1274
1275
1276/**
1277 * Generates and returns a string which is unique in the current document.
1278 * This is useful, for example, to create unique IDs for DOM elements.
1279 * @return {string} A unique id.
1280 */
1281goog.string.createUniqueString = function() {
1282 return 'goog_' + goog.string.uniqueStringCounter_++;
1283};
1284
1285
1286/**
1287 * Converts the supplied string to a number, which may be Infinity or NaN.
1288 * This function strips whitespace: (toNumber(' 123') === 123)
1289 * This function accepts scientific notation: (toNumber('1e1') === 10)
1290 *
1291 * This is better than Javascript's built-in conversions because, sadly:
1292 * (Number(' ') === 0) and (parseFloat('123a') === 123)
1293 *
1294 * @param {string} str The string to convert.
1295 * @return {number} The number the supplied string represents, or NaN.
1296 */
1297goog.string.toNumber = function(str) {
1298 var num = Number(str);
1299 if (num == 0 && goog.string.isEmpty(str)) {
1300 return NaN;
1301 }
1302 return num;
1303};
1304
1305
1306/**
1307 * Returns whether the given string is lower camel case (e.g. "isFooBar").
1308 *
1309 * Note that this assumes the string is entirely letters.
1310 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
1311 *
1312 * @param {string} str String to test.
1313 * @return {boolean} Whether the string is lower camel case.
1314 */
1315goog.string.isLowerCamelCase = function(str) {
1316 return /^[a-z]+([A-Z][a-z]*)*$/.test(str);
1317};
1318
1319
1320/**
1321 * Returns whether the given string is upper camel case (e.g. "FooBarBaz").
1322 *
1323 * Note that this assumes the string is entirely letters.
1324 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
1325 *
1326 * @param {string} str String to test.
1327 * @return {boolean} Whether the string is upper camel case.
1328 */
1329goog.string.isUpperCamelCase = function(str) {
1330 return /^([A-Z][a-z]*)+$/.test(str);
1331};
1332
1333
1334/**
1335 * Converts a string from selector-case to camelCase (e.g. from
1336 * "multi-part-string" to "multiPartString"), useful for converting
1337 * CSS selectors and HTML dataset keys to their equivalent JS properties.
1338 * @param {string} str The string in selector-case form.
1339 * @return {string} The string in camelCase form.
1340 */
1341goog.string.toCamelCase = function(str) {
1342 return String(str).replace(/\-([a-z])/g, function(all, match) {
1343 return match.toUpperCase();
1344 });
1345};
1346
1347
1348/**
1349 * Converts a string from camelCase to selector-case (e.g. from
1350 * "multiPartString" to "multi-part-string"), useful for converting JS
1351 * style and dataset properties to equivalent CSS selectors and HTML keys.
1352 * @param {string} str The string in camelCase form.
1353 * @return {string} The string in selector-case form.
1354 */
1355goog.string.toSelectorCase = function(str) {
1356 return String(str).replace(/([A-Z])/g, '-$1').toLowerCase();
1357};
1358
1359
1360/**
1361 * Converts a string into TitleCase. First character of the string is always
1362 * capitalized in addition to the first letter of every subsequent word.
1363 * Words are delimited by one or more whitespaces by default. Custom delimiters
1364 * can optionally be specified to replace the default, which doesn't preserve
1365 * whitespace delimiters and instead must be explicitly included if needed.
1366 *
1367 * Default delimiter => " ":
1368 * goog.string.toTitleCase('oneTwoThree') => 'OneTwoThree'
1369 * goog.string.toTitleCase('one two three') => 'One Two Three'
1370 * goog.string.toTitleCase(' one two ') => ' One Two '
1371 * goog.string.toTitleCase('one_two_three') => 'One_two_three'
1372 * goog.string.toTitleCase('one-two-three') => 'One-two-three'
1373 *
1374 * Custom delimiter => "_-.":
1375 * goog.string.toTitleCase('oneTwoThree', '_-.') => 'OneTwoThree'
1376 * goog.string.toTitleCase('one two three', '_-.') => 'One two three'
1377 * goog.string.toTitleCase(' one two ', '_-.') => ' one two '
1378 * goog.string.toTitleCase('one_two_three', '_-.') => 'One_Two_Three'
1379 * goog.string.toTitleCase('one-two-three', '_-.') => 'One-Two-Three'
1380 * goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three'
1381 * goog.string.toTitleCase('one. two. three', '_-.') => 'One. two. three'
1382 * goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three'
1383 *
1384 * @param {string} str String value in camelCase form.
1385 * @param {string=} opt_delimiters Custom delimiter character set used to
1386 * distinguish words in the string value. Each character represents a
1387 * single delimiter. When provided, default whitespace delimiter is
1388 * overridden and must be explicitly included if needed.
1389 * @return {string} String value in TitleCase form.
1390 */
1391goog.string.toTitleCase = function(str, opt_delimiters) {
1392 var delimiters = goog.isString(opt_delimiters) ?
1393 goog.string.regExpEscape(opt_delimiters) : '\\s';
1394
1395 // For IE8, we need to prevent using an empty character set. Otherwise,
1396 // incorrect matching will occur.
1397 delimiters = delimiters ? '|[' + delimiters + ']+' : '';
1398
1399 var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g');
1400 return str.replace(regexp, function(all, p1, p2) {
1401 return p1 + p2.toUpperCase();
1402 });
1403};
1404
1405
1406/**
1407 * Parse a string in decimal or hexidecimal ('0xFFFF') form.
1408 *
1409 * To parse a particular radix, please use parseInt(string, radix) directly. See
1410 * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt
1411 *
1412 * This is a wrapper for the built-in parseInt function that will only parse
1413 * numbers as base 10 or base 16. Some JS implementations assume strings
1414 * starting with "0" are intended to be octal. ES3 allowed but discouraged
1415 * this behavior. ES5 forbids it. This function emulates the ES5 behavior.
1416 *
1417 * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj
1418 *
1419 * @param {string|number|null|undefined} value The value to be parsed.
1420 * @return {number} The number, parsed. If the string failed to parse, this
1421 * will be NaN.
1422 */
1423goog.string.parseInt = function(value) {
1424 // Force finite numbers to strings.
1425 if (isFinite(value)) {
1426 value = String(value);
1427 }
1428
1429 if (goog.isString(value)) {
1430 // If the string starts with '0x' or '-0x', parse as hex.
1431 return /^\s*-?0x/i.test(value) ?
1432 parseInt(value, 16) : parseInt(value, 10);
1433 }
1434
1435 return NaN;
1436};
1437
1438
1439/**
1440 * Splits a string on a separator a limited number of times.
1441 *
1442 * This implementation is more similar to Python or Java, where the limit
1443 * parameter specifies the maximum number of splits rather than truncating
1444 * the number of results.
1445 *
1446 * See http://docs.python.org/2/library/stdtypes.html#str.split
1447 * See JavaDoc: http://goo.gl/F2AsY
1448 * See Mozilla reference: http://goo.gl/dZdZs
1449 *
1450 * @param {string} str String to split.
1451 * @param {string} separator The separator.
1452 * @param {number} limit The limit to the number of splits. The resulting array
1453 * will have a maximum length of limit+1. Negative numbers are the same
1454 * as zero.
1455 * @return {!Array.<string>} The string, split.
1456 */
1457
1458goog.string.splitLimit = function(str, separator, limit) {
1459 var parts = str.split(separator);
1460 var returnVal = [];
1461
1462 // Only continue doing this while we haven't hit the limit and we have
1463 // parts left.
1464 while (limit > 0 && parts.length) {
1465 returnVal.push(parts.shift());
1466 limit--;
1467 }
1468
1469 // If there are remaining parts, append them to the end.
1470 if (parts.length) {
1471 returnVal.push(parts.join(separator));
1472 }
1473
1474 return returnVal;
1475};
1476