yellow-brick-road 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/README.rst +10 -5
  2. data/lib/yellow-brick-road/directive_processor.rb +10 -3
  3. data/lib/yellow-brick-road/version.rb +1 -1
  4. data/test/dummy/app/assets/javascripts/closure-deps.js +3 -3
  5. data/test/dummy/log/development.log +159 -0
  6. data/test/dummy/tmp/cache/assets/C1E/0C0/sprockets%2F9c2f7430126aff8d8514326208712832 +165 -0
  7. data/test/dummy/tmp/cache/assets/C29/5D0/sprockets%2F1498f4451d0836a53c7c302c49920996 +0 -0
  8. data/test/dummy/tmp/cache/assets/C7E/9F0/sprockets%2F89862076204c62c4593ac20de32da909 +6 -6
  9. data/test/dummy/tmp/cache/assets/CB4/5B0/sprockets%2Ff17618b71eca9a4621f68626572a75b2 +103 -0
  10. data/test/dummy/tmp/cache/assets/CC2/240/sprockets%2F9f41da672330830c3366594f7ff5e7f6 +590 -0
  11. data/test/dummy/tmp/cache/assets/CC4/2C0/sprockets%2F715db78c7d974ea64406ed9711793f42 +494 -0
  12. data/test/dummy/tmp/cache/assets/CE1/610/sprockets%2F50c01109ecf86153176ccf577595fab4 +53 -0
  13. data/test/dummy/tmp/cache/assets/CE1/C20/sprockets%2F58a7b0499656c97a2204b5821eadab84 +207 -0
  14. data/test/dummy/tmp/cache/assets/CE4/DE0/sprockets%2Fb3aab0604ee02598f339965ec583a746 +204 -0
  15. data/test/dummy/tmp/cache/assets/CE9/B00/sprockets%2F80b139f66661444d6a781bfe56aee076 +0 -0
  16. data/test/dummy/tmp/cache/assets/CEB/840/sprockets%2F38fe57c2a5963f36a49404f672cc707b +0 -0
  17. data/test/dummy/tmp/cache/assets/CEC/6C0/sprockets%2F01b982295748dbd09683557aebd358bd +0 -0
  18. data/test/dummy/tmp/cache/assets/CF0/140/sprockets%2F3974b046fe949e86ef851a0271eb9d50 +1545 -0
  19. data/test/dummy/tmp/cache/assets/D01/DF0/sprockets%2F2ce268622b41a173bb209465a2ced6b9 +806 -0
  20. data/test/dummy/tmp/cache/assets/D05/920/sprockets%2F909507434dcc270db4853e4c147f0aac +4 -5
  21. data/test/dummy/tmp/cache/assets/D0A/C10/sprockets%2Fd050d5ee6a2073aa708a26c589a4c974 +283 -0
  22. data/test/dummy/tmp/cache/assets/D0F/360/sprockets%2Fa18927874a5e02c58c14c5b5df19ec04 +300 -0
  23. data/test/dummy/tmp/cache/assets/D15/700/sprockets%2F2987192f402d2bdc72792b5ae17f9a2f +142 -0
  24. data/test/dummy/tmp/cache/assets/D15/F60/sprockets%2Fa28394e3f80365b5bc86794dd46daa22 +0 -0
  25. data/test/dummy/tmp/cache/assets/D1F/E60/sprockets%2Fe65796b72231e36f5573ce71e2c8bbf8 +511 -0
  26. data/test/dummy/tmp/cache/assets/D22/200/sprockets%2F874bd1079c304ae88fbec8434d6d7794 +187 -0
  27. data/test/dummy/tmp/cache/assets/D3B/6A0/sprockets%2Ff8a978a3672b4f7ba513303b91ad15fc +127 -0
  28. data/test/dummy/tmp/cache/assets/D3C/530/sprockets%2Fe6730ff143273d9a7471b4afc3e1c19d +0 -0
  29. data/test/dummy/tmp/cache/assets/D41/B30/sprockets%2Fba1b93913dd01d83ac9a96df334456f8 +0 -0
  30. data/test/dummy/tmp/cache/assets/D49/570/sprockets%2Fd76be81d59871518ea06d3668f2c4bbb +239 -0
  31. data/test/dummy/tmp/cache/assets/D49/DA0/sprockets%2Fa84f85a305cde80a4d53785d7be0892f +457 -0
  32. data/test/dummy/tmp/cache/assets/D5C/760/sprockets%2F5cd491e0f9106cfd4ec4938896c97de7 +256 -0
  33. data/test/dummy/tmp/cache/assets/D5C/A00/sprockets%2Fefe291b3012745251e2641defbe4cad0 +1529 -0
  34. data/test/dummy/tmp/cache/assets/D68/2B0/sprockets%2Fe51ab0aca893e08c40f9672edef71106 +0 -0
  35. data/test/dummy/tmp/cache/assets/D6E/EA0/sprockets%2Fb52cbc47414c9e60ad4c46824928fbbe +115 -0
  36. data/test/dummy/tmp/cache/assets/D70/0B0/sprockets%2F1ae574bacfb86b4d51281b5e47fe1892 +505 -0
  37. data/test/dummy/tmp/cache/assets/D75/A60/sprockets%2Fab64285176f11f975fb6bb40af8bce76 +0 -0
  38. data/test/dummy/tmp/cache/assets/D76/0A0/sprockets%2F3ad3bd078c47096b34d5bcce886d7b47 +794 -0
  39. data/test/dummy/tmp/cache/assets/D79/C00/sprockets%2F94449fa386c370a1ebd7628eba9afe72 +72 -0
  40. data/test/dummy/tmp/cache/assets/D7B/310/sprockets%2Ff56e44be18b2d65efda80e588e5229a4 +0 -0
  41. data/test/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +7 -8
  42. data/test/dummy/tmp/cache/assets/D95/D20/sprockets%2F05b19351f203fb1eadf8ef1f0e6f9a60 +173 -0
  43. data/test/dummy/tmp/cache/assets/D9F/250/sprockets%2F40dcbb8f852f0e6360c4afb1f39964eb +0 -0
  44. data/test/dummy/tmp/cache/assets/DA8/0E0/sprockets%2F1351359f5bbdb94ef7d247df9af38bd1 +2556 -0
  45. data/test/dummy/tmp/cache/assets/DB3/070/sprockets%2Fd98f91680433cec456e6eb7485dcfdbc +522 -0
  46. data/test/dummy/tmp/cache/assets/DC4/6E0/sprockets%2F72117f09fccb98e6aac4cd1124edae42 +2539 -0
  47. data/test/dummy/tmp/cache/assets/DCC/D50/sprockets%2F9b2b027991c15af6f8afeacdd183c14e +1260 -0
  48. data/test/dummy/tmp/cache/assets/DD9/FF0/sprockets%2Fdf5dcfe86e199b272742a52a4b7e5fbd +823 -0
  49. data/test/dummy/tmp/cache/assets/DE7/0D0/sprockets%2Fe4d9fe29b6d96cdeb070d9b595af83d7 +1354 -0
  50. data/test/dummy/tmp/cache/assets/E00/3A0/sprockets%2Fc1db8cbfbc94bd2736f9f067a4c06cc7 +811 -0
  51. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  52. data/test/dummy/tmp/cache/assets/E0C/C80/sprockets%2F55805edb5f27aaef88eef6632fd08ade +1277 -0
  53. data/test/dummy/tmp/cache/assets/E1B/CF0/sprockets%2Feb58b29b94f29d7da8d9fbe666e4a8dd +474 -0
  54. data/test/dummy/tmp/cache/assets/E1E/E00/sprockets%2Fb005d4fa3dc6cfc1c5098e0fdb3f6b2b +1371 -0
  55. data/test/dummy/tmp/cache/assets/E30/8E0/sprockets%2Fef4fdb83b3eefb027cbc8e19b274ec80 +607 -0
  56. metadata +96 -8
@@ -0,0 +1,1260 @@
1
+ o: ActiveSupport::Cache::Entry :@compressedF:@expires_in0:@created_atf1325729947.427171: @value{ I" length:EFio�I" digest;
2
+ F"%72f11a08efac88291fbcab4e462a6d70I" source;
3
+ FI"o�// Copyright 2006 The Closure Library Authors. All Rights Reserved.
4
+ //
5
+ // Licensed under the Apache License, Version 2.0 (the "License");
6
+ // you may not use this file except in compliance with the License.
7
+ // You may obtain a copy of the License at
8
+ //
9
+ // http://www.apache.org/licenses/LICENSE-2.0
10
+ //
11
+ // Unless required by applicable law or agreed to in writing, software
12
+ // distributed under the License is distributed on an "AS-IS" BASIS,
13
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ // See the License for the specific language governing permissions and
15
+ // limitations under the License.
16
+
17
+ /**
18
+ * @fileoverview Utilities for string manipulation.
19
+ */
20
+
21
+
22
+ /**
23
+ * Namespace for string utilities
24
+ */
25
+
26
+ goog.provide('goog.string');
27
+ goog.provide('goog.string.Unicode');
28
+
29
+
30
+ /**
31
+ * Common Unicode string characters.
32
+ * @enum {string}
33
+ */
34
+ goog.string.Unicode = {
35
+ NBSP: '\xa0'
36
+ };
37
+
38
+
39
+ /**
40
+ * Fast prefix-checker.
41
+ * @param {string} str The string to check.
42
+ * @param {string} prefix A string to look for at the start of {@code str}.
43
+ * @return {boolean} True if {@code str} begins with {@code prefix}.
44
+ */
45
+ goog.string.startsWith = function(str, prefix) {
46
+ return str.lastIndexOf(prefix, 0) == 0;
47
+ };
48
+
49
+
50
+ /**
51
+ * Fast suffix-checker.
52
+ * @param {string} str The string to check.
53
+ * @param {string} suffix A string to look for at the end of {@code str}.
54
+ * @return {boolean} True if {@code str} ends with {@code suffix}.
55
+ */
56
+ goog.string.endsWith = function(str, suffix) {
57
+ var l = str.length - suffix.length;
58
+ return l >= 0 && str.indexOf(suffix, l) == l;
59
+ };
60
+
61
+
62
+ /**
63
+ * Case-insensitive prefix-checker.
64
+ * @param {string} str The string to check.
65
+ * @param {string} prefix A string to look for at the end of {@code str}.
66
+ * @return {boolean} True if {@code str} begins with {@code prefix} (ignoring
67
+ * case).
68
+ */
69
+ goog.string.caseInsensitiveStartsWith = function(str, prefix) {
70
+ return goog.string.caseInsensitiveCompare(
71
+ prefix, str.substr(0, prefix.length)) == 0;
72
+ };
73
+
74
+
75
+ /**
76
+ * Case-insensitive suffix-checker.
77
+ * @param {string} str The string to check.
78
+ * @param {string} suffix A string to look for at the end of {@code str}.
79
+ * @return {boolean} True if {@code str} ends with {@code suffix} (ignoring
80
+ * case).
81
+ */
82
+ goog.string.caseInsensitiveEndsWith = function(str, suffix) {
83
+ return goog.string.caseInsensitiveCompare(
84
+ suffix, str.substr(str.length - suffix.length, suffix.length)) == 0;
85
+ };
86
+
87
+
88
+ /**
89
+ * Does simple python-style string substitution.
90
+ * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog".
91
+ * @param {string} str The string containing the pattern.
92
+ * @param {...*} var_args The items to substitute into the pattern.
93
+ * @return {string} A copy of {@code str} in which each occurrence of
94
+ * {@code %s} has been replaced an argument from {@code var_args}.
95
+ */
96
+ goog.string.subs = function(str, var_args) {
97
+ // This appears to be slow, but testing shows it compares more or less
98
+ // equivalent to the regex.exec method.
99
+ for (var i = 1; i < arguments.length; i++) {
100
+ // We cast to String in case an argument is a Function. Replacing $&, for
101
+ // example, with $$$& stops the replace from subsituting the whole match
102
+ // into the resultant string. $$$& in the first replace becomes $$& in the
103
+ // second, which leaves $& in the resultant string. Also:
104
+ // $$, $`, $', $n $nn
105
+ var replacement = String(arguments[i]).replace(/\$/g, '$$$$');
106
+ str = str.replace(/\%s/, replacement);
107
+ }
108
+ return str;
109
+ };
110
+
111
+
112
+ /**
113
+ * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines
114
+ * and tabs) to a single space, and strips leading and trailing whitespace.
115
+ * @param {string} str Input string.
116
+ * @return {string} A copy of {@code str} with collapsed whitespace.
117
+ */
118
+ goog.string.collapseWhitespace = function(str) {
119
+ // Since IE doesn't include non-breaking-space (0xa0) in their \s character
120
+ // class (as required by section 7.2 of the ECMAScript spec), we explicitly
121
+ // include it in the regexp to enforce consistent cross-browser behavior.
122
+ return str.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, '');
123
+ };
124
+
125
+
126
+ /**
127
+ * Checks if a string is empty or contains only whitespaces.
128
+ * @param {string} str The string to check.
129
+ * @return {boolean} True if {@code str} is empty or whitespace only.
130
+ */
131
+ goog.string.isEmpty = function(str) {
132
+ // testing length == 0 first is actually slower in all browsers (about the
133
+ // same in Opera).
134
+ // Since IE doesn't include non-breaking-space (0xa0) in their \s character
135
+ // class (as required by section 7.2 of the ECMAScript spec), we explicitly
136
+ // include it in the regexp to enforce consistent cross-browser behavior.
137
+ return /^[\s\xa0]*$/.test(str);
138
+ };
139
+
140
+
141
+ /**
142
+ * Checks if a string is null, empty or contains only whitespaces.
143
+ * @param {*} str The string to check.
144
+ * @return {boolean} True if{@code str} is null, empty, or whitespace only.
145
+ */
146
+ goog.string.isEmptySafe = function(str) {
147
+ return goog.string.isEmpty(goog.string.makeSafe(str));
148
+ };
149
+
150
+
151
+ /**
152
+ * Checks if a string is all breaking whitespace.
153
+ * @param {string} str The string to check.
154
+ * @return {boolean} Whether the string is all breaking whitespace.
155
+ */
156
+ goog.string.isBreakingWhitespace = function(str) {
157
+ return !/[^\t\n\r ]/.test(str);
158
+ };
159
+
160
+
161
+ /**
162
+ * Checks if a string contains all letters.
163
+ * @param {string} str string to check.
164
+ * @return {boolean} True if {@code str} consists entirely of letters.
165
+ */
166
+ goog.string.isAlpha = function(str) {
167
+ return !/[^a-zA-Z]/.test(str);
168
+ };
169
+
170
+
171
+ /**
172
+ * Checks if a string contains only numbers.
173
+ * @param {*} str string to check. If not a string, it will be
174
+ * casted to one.
175
+ * @return {boolean} True if {@code str} is numeric.
176
+ */
177
+ goog.string.isNumeric = function(str) {
178
+ return !/[^0-9]/.test(str);
179
+ };
180
+
181
+
182
+ /**
183
+ * Checks if a string contains only numbers or letters.
184
+ * @param {string} str string to check.
185
+ * @return {boolean} True if {@code str} is alphanumeric.
186
+ */
187
+ goog.string.isAlphaNumeric = function(str) {
188
+ return !/[^a-zA-Z0-9]/.test(str);
189
+ };
190
+
191
+
192
+ /**
193
+ * Checks if a character is a space character.
194
+ * @param {string} ch Character to check.
195
+ * @return {boolean} True if {code ch} is a space.
196
+ */
197
+ goog.string.isSpace = function(ch) {
198
+ return ch == ' ';
199
+ };
200
+
201
+
202
+ /**
203
+ * Checks if a character is a valid unicode character.
204
+ * @param {string} ch Character to check.
205
+ * @return {boolean} True if {code ch} is a valid unicode character.
206
+ */
207
+ goog.string.isUnicodeChar = function(ch) {
208
+ return ch.length == 1 && ch >= ' ' && ch <= '~' ||
209
+ ch >= '\u0080' && ch <= '\uFFFD';
210
+ };
211
+
212
+
213
+ /**
214
+ * Takes a string and replaces newlines with a space. Multiple lines are
215
+ * replaced with a single space.
216
+ * @param {string} str The string from which to strip newlines.
217
+ * @return {string} A copy of {@code str} stripped of newlines.
218
+ */
219
+ goog.string.stripNewlines = function(str) {
220
+ return str.replace(/(\r\n|\r|\n)+/g, ' ');
221
+ };
222
+
223
+
224
+ /**
225
+ * Replaces Windows and Mac new lines with unix style: \r or \r\n with \n.
226
+ * @param {string} str The string to in which to canonicalize newlines.
227
+ * @return {string} {@code str} A copy of {@code} with canonicalized newlines.
228
+ */
229
+ goog.string.canonicalizeNewlines = function(str) {
230
+ return str.replace(/(\r\n|\r|\n)/g, '\n');
231
+ };
232
+
233
+
234
+ /**
235
+ * Normalizes whitespace in a string, replacing all whitespace chars with
236
+ * a space.
237
+ * @param {string} str The string in which to normalize whitespace.
238
+ * @return {string} A copy of {@code str} with all whitespace normalized.
239
+ */
240
+ goog.string.normalizeWhitespace = function(str) {
241
+ return str.replace(/\xa0|\s/g, ' ');
242
+ };
243
+
244
+
245
+ /**
246
+ * Normalizes spaces in a string, replacing all consecutive spaces and tabs
247
+ * with a single space. Replaces non-breaking space with a space.
248
+ * @param {string} str The string in which to normalize spaces.
249
+ * @return {string} A copy of {@code str} with all consecutive spaces and tabs
250
+ * replaced with a single space.
251
+ */
252
+ goog.string.normalizeSpaces = function(str) {
253
+ return str.replace(/\xa0|[ \t]+/g, ' ');
254
+ };
255
+
256
+
257
+ /**
258
+ * Removes the breaking spaces from the left and right of the string and
259
+ * collapses the sequences of breaking spaces in the middle into single spaces.
260
+ * The original and the result strings render the same way in HTML.
261
+ * @param {string} str A string in which to collapse spaces.
262
+ * @return {string} Copy of the string with normalized breaking spaces.
263
+ */
264
+ goog.string.collapseBreakingSpaces = function(str) {
265
+ return str.replace(/[\t\r\n ]+/g, ' ').replace(
266
+ /^[\t\r\n ]+|[\t\r\n ]+$/g, '');
267
+ };
268
+
269
+
270
+ /**
271
+ * Trims white spaces to the left and right of a string.
272
+ * @param {string} str The string to trim.
273
+ * @return {string} A trimmed copy of {@code str}.
274
+ */
275
+ goog.string.trim = function(str) {
276
+ // Since IE doesn't include non-breaking-space (0xa0) in their \s character
277
+ // class (as required by section 7.2 of the ECMAScript spec), we explicitly
278
+ // include it in the regexp to enforce consistent cross-browser behavior.
279
+ return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
280
+ };
281
+
282
+
283
+ /**
284
+ * Trims whitespaces at the left end of a string.
285
+ * @param {string} str The string to left trim.
286
+ * @return {string} A trimmed copy of {@code str}.
287
+ */
288
+ goog.string.trimLeft = function(str) {
289
+ // Since IE doesn't include non-breaking-space (0xa0) in their \s character
290
+ // class (as required by section 7.2 of the ECMAScript spec), we explicitly
291
+ // include it in the regexp to enforce consistent cross-browser behavior.
292
+ return str.replace(/^[\s\xa0]+/, '');
293
+ };
294
+
295
+
296
+ /**
297
+ * Trims whitespaces at the right end of a string.
298
+ * @param {string} str The string to right trim.
299
+ * @return {string} A trimmed copy of {@code str}.
300
+ */
301
+ goog.string.trimRight = function(str) {
302
+ // Since IE doesn't include non-breaking-space (0xa0) in their \s character
303
+ // class (as required by section 7.2 of the ECMAScript spec), we explicitly
304
+ // include it in the regexp to enforce consistent cross-browser behavior.
305
+ return str.replace(/[\s\xa0]+$/, '');
306
+ };
307
+
308
+
309
+ /**
310
+ * A string comparator that ignores case.
311
+ * -1 = str1 less than str2
312
+ * 0 = str1 equals str2
313
+ * 1 = str1 greater than str2
314
+ *
315
+ * @param {string} str1 The string to compare.
316
+ * @param {string} str2 The string to compare {@code str1} to.
317
+ * @return {number} The comparator result, as described above.
318
+ */
319
+ goog.string.caseInsensitiveCompare = function(str1, str2) {
320
+ var test1 = String(str1).toLowerCase();
321
+ var test2 = String(str2).toLowerCase();
322
+
323
+ if (test1 < test2) {
324
+ return -1;
325
+ } else if (test1 == test2) {
326
+ return 0;
327
+ } else {
328
+ return 1;
329
+ }
330
+ };
331
+
332
+
333
+ /**
334
+ * Regular expression used for splitting a string into substrings of fractional
335
+ * numbers, integers, and non-numeric characters.
336
+ * @type {RegExp}
337
+ * @private
338
+ */
339
+ goog.string.numerateCompareRegExp_ = /(\.\d+)|(\d+)|(\D+)/g;
340
+
341
+
342
+ /**
343
+ * String comparison function that handles numbers in a way humans might expect.
344
+ * Using this function, the string "File 2.jpg" sorts before "File 10.jpg". The
345
+ * comparison is mostly case-insensitive, though strings that are identical
346
+ * except for case are sorted with the upper-case strings before lower-case.
347
+ *
348
+ * This comparison function is significantly slower (about 500x) than either
349
+ * the default or the case-insensitive compare. It should not be used in
350
+ * time-critical code, but should be fast enough to sort several hundred short
351
+ * strings (like filenames) with a reasonable delay.
352
+ *
353
+ * @param {string} str1 The string to compare in a numerically sensitive way.
354
+ * @param {string} str2 The string to compare {@code str1} to.
355
+ * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
356
+ * 0 if str1 > str2.
357
+ */
358
+ goog.string.numerateCompare = function(str1, str2) {
359
+ if (str1 == str2) {
360
+ return 0;
361
+ }
362
+ if (!str1) {
363
+ return -1;
364
+ }
365
+ if (!str2) {
366
+ return 1;
367
+ }
368
+
369
+ // Using match to split the entire string ahead of time turns out to be faster
370
+ // for most inputs than using RegExp.exec or iterating over each character.
371
+ var tokens1 = str1.toLowerCase().match(goog.string.numerateCompareRegExp_);
372
+ var tokens2 = str2.toLowerCase().match(goog.string.numerateCompareRegExp_);
373
+
374
+ var count = Math.min(tokens1.length, tokens2.length);
375
+
376
+ for (var i = 0; i < count; i++) {
377
+ var a = tokens1[i];
378
+ var b = tokens2[i];
379
+
380
+ // Compare pairs of tokens, returning if one token sorts before the other.
381
+ if (a != b) {
382
+
383
+ // Only if both tokens are integers is a special comparison required.
384
+ // Decimal numbers are sorted as strings (e.g., '.09' < '.1').
385
+ var num1 = parseInt(a, 10);
386
+ if (!isNaN(num1)) {
387
+ var num2 = parseInt(b, 10);
388
+ if (!isNaN(num2) && num1 - num2) {
389
+ return num1 - num2;
390
+ }
391
+ }
392
+ return a < b ? -1 : 1;
393
+ }
394
+ }
395
+
396
+ // If one string is a substring of the other, the shorter string sorts first.
397
+ if (tokens1.length != tokens2.length) {
398
+ return tokens1.length - tokens2.length;
399
+ }
400
+
401
+ // The two strings must be equivalent except for case (perfect equality is
402
+ // tested at the head of the function.) Revert to default ASCII-betical string
403
+ // comparison to stablize the sort.
404
+ return str1 < str2 ? -1 : 1;
405
+ };
406
+
407
+
408
+ /**
409
+ * Regular expression used for determining if a string needs to be encoded.
410
+ * @type {RegExp}
411
+ * @private
412
+ */
413
+ goog.string.encodeUriRegExp_ = /^[a-zA-Z0-9\-_.!~*'()]*$/;
414
+
415
+
416
+ /**
417
+ * URL-encodes a string
418
+ * @param {*} str The string to url-encode.
419
+ * @return {string} An encoded copy of {@code str} that is safe for urls.
420
+ * Note that '#', ':', and other characters used to delimit portions
421
+ * of URLs *will* be encoded.
422
+ */
423
+ goog.string.urlEncode = function(str) {
424
+ str = String(str);
425
+ // Checking if the search matches before calling encodeURIComponent avoids an
426
+ // extra allocation in IE6. This adds about 10us time in FF and a similiar
427
+ // over head in IE6 for lower working set apps, but for large working set
428
+ // apps like Gmail, it saves about 70us per call.
429
+ if (!goog.string.encodeUriRegExp_.test(str)) {
430
+ return encodeURIComponent(str);
431
+ }
432
+ return str;
433
+ };
434
+
435
+
436
+ /**
437
+ * URL-decodes the string. We need to specially handle '+'s because
438
+ * the javascript library doesn't convert them to spaces.
439
+ * @param {string} str The string to url decode.
440
+ * @return {string} The decoded {@code str}.
441
+ */
442
+ goog.string.urlDecode = function(str) {
443
+ return decodeURIComponent(str.replace(/\+/g, ' '));
444
+ };
445
+
446
+
447
+ /**
448
+ * Converts \n to <br>s or <br />s.
449
+ * @param {string} str The string in which to convert newlines.
450
+ * @param {boolean=} opt_xml Whether to use XML compatible tags.
451
+ * @return {string} A copy of {@code str} with converted newlines.
452
+ */
453
+ goog.string.newLineToBr = function(str, opt_xml) {
454
+ return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');
455
+ };
456
+
457
+
458
+ /**
459
+ * Escape double quote '"' characters in addition to '&', '<', and '>' so that a
460
+ * string can be included in an HTML tag attribute value within double quotes.
461
+ *
462
+ * It should be noted that > doesn't need to be escaped for the HTML or XML to
463
+ * be valid, but it has been decided to escape it for consistency with other
464
+ * implementations.
465
+ *
466
+ * NOTE(user):
467
+ * HtmlEscape is often called during the generation of large blocks of HTML.
468
+ * Using statics for the regular expressions and strings is an optimization
469
+ * that can more than half the amount of time IE spends in this function for
470
+ * large apps, since strings and regexes both contribute to GC allocations.
471
+ *
472
+ * Testing for the presence of a character before escaping increases the number
473
+ * of function calls, but actually provides a speed increase for the average
474
+ * case -- since the average case often doesn't require the escaping of all 4
475
+ * characters and indexOf() is much cheaper than replace().
476
+ * The worst case does suffer slightly from the additional calls, therefore the
477
+ * opt_isLikelyToContainHtmlChars option has been included for situations
478
+ * where all 4 HTML entities are very likely to be present and need escaping.
479
+ *
480
+ * Some benchmarks (times tended to fluctuate +-0.05ms):
481
+ * FireFox IE6
482
+ * (no chars / average (mix of cases) / all 4 chars)
483
+ * no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80
484
+ * indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84
485
+ * indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85
486
+ *
487
+ * An additional advantage of checking if replace actually needs to be called
488
+ * is a reduction in the number of object allocations, so as the size of the
489
+ * application grows the difference between the various methods would increase.
490
+ *
491
+ * @param {string} str string to be escaped.
492
+ * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see
493
+ * if the character needs replacing - use this option if you expect each of
494
+ * the characters to appear often. Leave false if you expect few html
495
+ * characters to occur in your strings, such as if you are escaping HTML.
496
+ * @return {string} An escaped copy of {@code str}.
497
+ */
498
+ goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {
499
+
500
+ if (opt_isLikelyToContainHtmlChars) {
501
+ return str.replace(goog.string.amperRe_, '&amp;')
502
+ .replace(goog.string.ltRe_, '&lt;')
503
+ .replace(goog.string.gtRe_, '&gt;')
504
+ .replace(goog.string.quotRe_, '&quot;');
505
+
506
+ } else {
507
+ // quick test helps in the case when there are no chars to replace, in
508
+ // worst case this makes barely a difference to the time taken
509
+ if (!goog.string.allRe_.test(str)) return str;
510
+
511
+ // str.indexOf is faster than regex.test in this case
512
+ if (str.indexOf('&') != -1) {
513
+ str = str.replace(goog.string.amperRe_, '&amp;');
514
+ }
515
+ if (str.indexOf('<') != -1) {
516
+ str = str.replace(goog.string.ltRe_, '&lt;');
517
+ }
518
+ if (str.indexOf('>') != -1) {
519
+ str = str.replace(goog.string.gtRe_, '&gt;');
520
+ }
521
+ if (str.indexOf('"') != -1) {
522
+ str = str.replace(goog.string.quotRe_, '&quot;');
523
+ }
524
+ return str;
525
+ }
526
+ };
527
+
528
+
529
+ /**
530
+ * Regular expression that matches an ampersand, for use in escaping.
531
+ * @type {RegExp}
532
+ * @private
533
+ */
534
+ goog.string.amperRe_ = /&/g;
535
+
536
+
537
+ /**
538
+ * Regular expression that matches a less than sign, for use in escaping.
539
+ * @type {RegExp}
540
+ * @private
541
+ */
542
+ goog.string.ltRe_ = /</g;
543
+
544
+
545
+ /**
546
+ * Regular expression that matches a greater than sign, for use in escaping.
547
+ * @type {RegExp}
548
+ * @private
549
+ */
550
+ goog.string.gtRe_ = />/g;
551
+
552
+
553
+ /**
554
+ * Regular expression that matches a double quote, for use in escaping.
555
+ * @type {RegExp}
556
+ * @private
557
+ */
558
+ goog.string.quotRe_ = /\"/g;
559
+
560
+
561
+ /**
562
+ * Regular expression that matches any character that needs to be escaped.
563
+ * @type {RegExp}
564
+ * @private
565
+ */
566
+ goog.string.allRe_ = /[&<>\"]/;
567
+
568
+
569
+ /**
570
+ * Unescapes an HTML string.
571
+ *
572
+ * @param {string} str The string to unescape.
573
+ * @return {string} An unescaped copy of {@code str}.
574
+ */
575
+ goog.string.unescapeEntities = function(str) {
576
+ if (goog.string.contains(str, '&')) {
577
+ // We are careful not to use a DOM if we do not have one. We use the []
578
+ // notation so that the JSCompiler will not complain about these objects and
579
+ // fields in the case where we have no DOM.
580
+ if ('document' in goog.global) {
581
+ return goog.string.unescapeEntitiesUsingDom_(str);
582
+ } else {
583
+ // Fall back on pure XML entities
584
+ return goog.string.unescapePureXmlEntities_(str);
585
+ }
586
+ }
587
+ return str;
588
+ };
589
+
590
+
591
+ /**
592
+ * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric
593
+ * entities. This function is XSS-safe and whitespace-preserving.
594
+ * @private
595
+ * @param {string} str The string to unescape.
596
+ * @return {string} The unescaped {@code str} string.
597
+ */
598
+ goog.string.unescapeEntitiesUsingDom_ = function(str) {
599
+ var seen = {'&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"'};
600
+ var div = document.createElement('div');
601
+ // Match as many valid entity characters as possible. If the actual entity
602
+ // happens to be shorter, it will still work as innerHTML will return the
603
+ // trailing characters unchanged. Since the entity characters do not include
604
+ // open angle bracket, there is no chance of XSS from the innerHTML use.
605
+ // Since no whitespace is passed to innerHTML, whitespace is preserved.
606
+ return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {
607
+ // Check for cached entity.
608
+ var value = seen[s];
609
+ if (value) {
610
+ return value;
611
+ }
612
+ // Check for numeric entity.
613
+ if (entity.charAt(0) == '#') {
614
+ // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex numbers.
615
+ var n = Number('0' + entity.substr(1));
616
+ if (!isNaN(n)) {
617
+ value = String.fromCharCode(n);
618
+ }
619
+ }
620
+ // Fall back to innerHTML otherwise.
621
+ if (!value) {
622
+ // Append a non-entity character to avoid a bug in Webkit that parses
623
+ // an invalid entity at the end of innerHTML text as the empty string.
624
+ div.innerHTML = s + ' ';
625
+ // Then remove the trailing character from the result.
626
+ value = div.firstChild.nodeValue.slice(0, -1);
627
+ }
628
+ // Cache and return.
629
+ return seen[s] = value;
630
+ });
631
+ };
632
+
633
+
634
+ /**
635
+ * Unescapes XML entities.
636
+ * @private
637
+ * @param {string} str The string to unescape.
638
+ * @return {string} An unescaped copy of {@code str}.
639
+ */
640
+ goog.string.unescapePureXmlEntities_ = function(str) {
641
+ return str.replace(/&([^;]+);/g, function(s, entity) {
642
+ switch (entity) {
643
+ case 'amp':
644
+ return '&';
645
+ case 'lt':
646
+ return '<';
647
+ case 'gt':
648
+ return '>';
649
+ case 'quot':
650
+ return '"';
651
+ default:
652
+ if (entity.charAt(0) == '#') {
653
+ // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex.
654
+ var n = Number('0' + entity.substr(1));
655
+ if (!isNaN(n)) {
656
+ return String.fromCharCode(n);
657
+ }
658
+ }
659
+ // For invalid entities we just return the entity
660
+ return s;
661
+ }
662
+ });
663
+ };
664
+
665
+
666
+ /**
667
+ * Regular expression that matches an HTML entity.
668
+ * See also HTML5: Tokenization / Tokenizing character references.
669
+ * @private
670
+ * @type {!RegExp}
671
+ */
672
+ goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g;
673
+
674
+
675
+ /**
676
+ * Do escaping of whitespace to preserve spatial formatting. We use character
677
+ * entity #160 to make it safer for xml.
678
+ * @param {string} str The string in which to escape whitespace.
679
+ * @param {boolean=} opt_xml Whether to use XML compatible tags.
680
+ * @return {string} An escaped copy of {@code str}.
681
+ */
682
+ goog.string.whitespaceEscape = function(str, opt_xml) {
683
+ return goog.string.newLineToBr(str.replace(/ /g, ' &#160;'), opt_xml);
684
+ };
685
+
686
+
687
+ /**
688
+ * Strip quote characters around a string. The second argument is a string of
689
+ * characters to treat as quotes. This can be a single character or a string of
690
+ * multiple character and in that case each of those are treated as possible
691
+ * quote characters. For example:
692
+ *
693
+ * <pre>
694
+ * goog.string.stripQuotes('"abc"', '"`') --> 'abc'
695
+ * goog.string.stripQuotes('`abc`', '"`') --> 'abc'
696
+ * </pre>
697
+ *
698
+ * @param {string} str The string to strip.
699
+ * @param {string} quoteChars The quote characters to strip.
700
+ * @return {string} A copy of {@code str} without the quotes.
701
+ */
702
+ goog.string.stripQuotes = function(str, quoteChars) {
703
+ var length = quoteChars.length;
704
+ for (var i = 0; i < length; i++) {
705
+ var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i);
706
+ if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) {
707
+ return str.substring(1, str.length - 1);
708
+ }
709
+ }
710
+ return str;
711
+ };
712
+
713
+
714
+ /**
715
+ * Truncates a string to a certain length and adds '...' if necessary. The
716
+ * length also accounts for the ellipsis, so a maximum length of 10 and a string
717
+ * 'Hello World!' produces 'Hello W...'.
718
+ * @param {string} str The string to truncate.
719
+ * @param {number} chars Max number of characters.
720
+ * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
721
+ * characters from being cut off in the middle.
722
+ * @return {string} The truncated {@code str} string.
723
+ */
724
+ goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) {
725
+ if (opt_protectEscapedCharacters) {
726
+ str = goog.string.unescapeEntities(str);
727
+ }
728
+
729
+ if (str.length > chars) {
730
+ str = str.substring(0, chars - 3) + '...';
731
+ }
732
+
733
+ if (opt_protectEscapedCharacters) {
734
+ str = goog.string.htmlEscape(str);
735
+ }
736
+
737
+ return str;
738
+ };
739
+
740
+
741
+ /**
742
+ * Truncate a string in the middle, adding "..." if necessary,
743
+ * and favoring the beginning of the string.
744
+ * @param {string} str The string to truncate the middle of.
745
+ * @param {number} chars Max number of characters.
746
+ * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
747
+ * characters from being cutoff in the middle.
748
+ * @param {number=} opt_trailingChars Optional number of trailing characters to
749
+ * leave at the end of the string, instead of truncating as close to the
750
+ * middle as possible.
751
+ * @return {string} A truncated copy of {@code str}.
752
+ */
753
+ goog.string.truncateMiddle = function(str, chars,
754
+ opt_protectEscapedCharacters, opt_trailingChars) {
755
+ if (opt_protectEscapedCharacters) {
756
+ str = goog.string.unescapeEntities(str);
757
+ }
758
+
759
+ if (opt_trailingChars && str.length > chars) {
760
+ if (opt_trailingChars > chars) {
761
+ opt_trailingChars = chars;
762
+ }
763
+ var endPoint = str.length - opt_trailingChars;
764
+ var startPoint = chars - opt_trailingChars;
765
+ str = str.substring(0, startPoint) + '...' + str.substring(endPoint);
766
+ } else if (str.length > chars) {
767
+ // Favor the beginning of the string:
768
+ var half = Math.floor(chars / 2);
769
+ var endPos = str.length - half;
770
+ half += chars % 2;
771
+ str = str.substring(0, half) + '...' + str.substring(endPos);
772
+ }
773
+
774
+ if (opt_protectEscapedCharacters) {
775
+ str = goog.string.htmlEscape(str);
776
+ }
777
+
778
+ return str;
779
+ };
780
+
781
+
782
+ /**
783
+ * Special chars that need to be escaped for goog.string.quote.
784
+ * @private
785
+ * @type {Object}
786
+ */
787
+ goog.string.specialEscapeChars_ = {
788
+ '\0': '\\0',
789
+ '\b': '\\b',
790
+ '\f': '\\f',
791
+ '\n': '\\n',
792
+ '\r': '\\r',
793
+ '\t': '\\t',
794
+ '\x0B': '\\x0B', // '\v' is not supported in JScript
795
+ '"': '\\"',
796
+ '\\': '\\\\'
797
+ };
798
+
799
+
800
+ /**
801
+ * Character mappings used internally for goog.string.escapeChar.
802
+ * @private
803
+ * @type {Object}
804
+ */
805
+ goog.string.jsEscapeCache_ = {
806
+ '\'': '\\\''
807
+ };
808
+
809
+
810
+ /**
811
+ * Encloses a string in double quotes and escapes characters so that the
812
+ * string is a valid JS string.
813
+ * @param {string} s The string to quote.
814
+ * @return {string} A copy of {@code s} surrounded by double quotes.
815
+ */
816
+ goog.string.quote = function(s) {
817
+ s = String(s);
818
+ if (s.quote) {
819
+ return s.quote();
820
+ } else {
821
+ var sb = ['"'];
822
+ for (var i = 0; i < s.length; i++) {
823
+ var ch = s.charAt(i);
824
+ var cc = ch.charCodeAt(0);
825
+ sb[i + 1] = goog.string.specialEscapeChars_[ch] ||
826
+ ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch));
827
+ }
828
+ sb.push('"');
829
+ return sb.join('');
830
+ }
831
+ };
832
+
833
+
834
+ /**
835
+ * Takes a string and returns the escaped string for that character.
836
+ * @param {string} str The string to escape.
837
+ * @return {string} An escaped string representing {@code str}.
838
+ */
839
+ goog.string.escapeString = function(str) {
840
+ var sb = [];
841
+ for (var i = 0; i < str.length; i++) {
842
+ sb[i] = goog.string.escapeChar(str.charAt(i));
843
+ }
844
+ return sb.join('');
845
+ };
846
+
847
+
848
+ /**
849
+ * Takes a character and returns the escaped string for that character. For
850
+ * example escapeChar(String.fromCharCode(15)) -> "\\x0E".
851
+ * @param {string} c The character to escape.
852
+ * @return {string} An escaped string representing {@code c}.
853
+ */
854
+ goog.string.escapeChar = function(c) {
855
+ if (c in goog.string.jsEscapeCache_) {
856
+ return goog.string.jsEscapeCache_[c];
857
+ }
858
+
859
+ if (c in goog.string.specialEscapeChars_) {
860
+ return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c];
861
+ }
862
+
863
+ var rv = c;
864
+ var cc = c.charCodeAt(0);
865
+ if (cc > 31 && cc < 127) {
866
+ rv = c;
867
+ } else {
868
+ // tab is 9 but handled above
869
+ if (cc < 256) {
870
+ rv = '\\x';
871
+ if (cc < 16 || cc > 256) {
872
+ rv += '0';
873
+ }
874
+ } else {
875
+ rv = '\\u';
876
+ if (cc < 4096) { // \u1000
877
+ rv += '0';
878
+ }
879
+ }
880
+ rv += cc.toString(16).toUpperCase();
881
+ }
882
+
883
+ return goog.string.jsEscapeCache_[c] = rv;
884
+ };
885
+
886
+
887
+ /**
888
+ * Takes a string and creates a map (Object) in which the keys are the
889
+ * characters in the string. The value for the key is set to true. You can
890
+ * then use goog.object.map or goog.array.map to change the values.
891
+ * @param {string} s The string to build the map from.
892
+ * @return {Object} The map of characters used.
893
+ */
894
+ // TODO(arv): It seems like we should have a generic goog.array.toMap. But do
895
+ // we want a dependency on goog.array in goog.string?
896
+ goog.string.toMap = function(s) {
897
+ var rv = {};
898
+ for (var i = 0; i < s.length; i++) {
899
+ rv[s.charAt(i)] = true;
900
+ }
901
+ return rv;
902
+ };
903
+
904
+
905
+ /**
906
+ * Checks whether a string contains a given character.
907
+ * @param {string} s The string to test.
908
+ * @param {string} ss The substring to test for.
909
+ * @return {boolean} True if {@code s} contains {@code ss}.
910
+ */
911
+ goog.string.contains = function(s, ss) {
912
+ return s.indexOf(ss) != -1;
913
+ };
914
+
915
+
916
+ /**
917
+ * Returns the non-overlapping occurrences of ss in s.
918
+ * If either s or ss evalutes to false, then returns zero.
919
+ * @param {string} s The string to look in.
920
+ * @param {string} ss The string to look for.
921
+ * @return {number} Number of occurrences of ss in s.
922
+ */
923
+ goog.string.countOf = function(s, ss) {
924
+ return s && ss ? s.split(ss).length - 1 : 0;
925
+ };
926
+
927
+
928
+ /**
929
+ * Removes a substring of a specified length at a specific
930
+ * index in a string.
931
+ * @param {string} s The base string from which to remove.
932
+ * @param {number} index The index at which to remove the substring.
933
+ * @param {number} stringLength The length of the substring to remove.
934
+ * @return {string} A copy of {@code s} with the substring removed or the full
935
+ * string if nothing is removed or the input is invalid.
936
+ */
937
+ goog.string.removeAt = function(s, index, stringLength) {
938
+ var resultStr = s;
939
+ // If the index is greater or equal to 0 then remove substring
940
+ if (index >= 0 && index < s.length && stringLength > 0) {
941
+ resultStr = s.substr(0, index) +
942
+ s.substr(index + stringLength, s.length - index - stringLength);
943
+ }
944
+ return resultStr;
945
+ };
946
+
947
+
948
+ /**
949
+ * Removes the first occurrence of a substring from a string.
950
+ * @param {string} s The base string from which to remove.
951
+ * @param {string} ss The string to remove.
952
+ * @return {string} A copy of {@code s} with {@code ss} removed or the full
953
+ * string if nothing is removed.
954
+ */
955
+ goog.string.remove = function(s, ss) {
956
+ var re = new RegExp(goog.string.regExpEscape(ss), '');
957
+ return s.replace(re, '');
958
+ };
959
+
960
+
961
+ /**
962
+ * Removes all occurrences of a substring from a string.
963
+ * @param {string} s The base string from which to remove.
964
+ * @param {string} ss The string to remove.
965
+ * @return {string} A copy of {@code s} with {@code ss} removed or the full
966
+ * string if nothing is removed.
967
+ */
968
+ goog.string.removeAll = function(s, ss) {
969
+ var re = new RegExp(goog.string.regExpEscape(ss), 'g');
970
+ return s.replace(re, '');
971
+ };
972
+
973
+
974
+ /**
975
+ * Escapes characters in the string that are not safe to use in a RegExp.
976
+ * @param {*} s The string to escape. If not a string, it will be casted
977
+ * to one.
978
+ * @return {string} A RegExp safe, escaped copy of {@code s}.
979
+ */
980
+ goog.string.regExpEscape = function(s) {
981
+ return String(s).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
982
+ replace(/\x08/g, '\\x08');
983
+ };
984
+
985
+
986
+ /**
987
+ * Repeats a string n times.
988
+ * @param {string} string The string to repeat.
989
+ * @param {number} length The number of times to repeat.
990
+ * @return {string} A string containing {@code length} repetitions of
991
+ * {@code string}.
992
+ */
993
+ goog.string.repeat = function(string, length) {
994
+ return new Array(length + 1).join(string);
995
+ };
996
+
997
+
998
+ /**
999
+ * Pads number to given length and optionally rounds it to a given precision.
1000
+ * For example:
1001
+ * <pre>padNumber(1.25, 2, 3) -> '01.250'
1002
+ * padNumber(1.25, 2) -> '01.25'
1003
+ * padNumber(1.25, 2, 1) -> '01.3'
1004
+ * padNumber(1.25, 0) -> '1.25'</pre>
1005
+ *
1006
+ * @param {number} num The number to pad.
1007
+ * @param {number} length The desired length.
1008
+ * @param {number=} opt_precision The desired precision.
1009
+ * @return {string} {@code num} as a string with the given options.
1010
+ */
1011
+ goog.string.padNumber = function(num, length, opt_precision) {
1012
+ var s = goog.isDef(opt_precision) ? num.toFixed(opt_precision) : String(num);
1013
+ var index = s.indexOf('.');
1014
+ if (index == -1) {
1015
+ index = s.length;
1016
+ }
1017
+ return goog.string.repeat('0', Math.max(0, length - index)) + s;
1018
+ };
1019
+
1020
+
1021
+ /**
1022
+ * Returns a string representation of the given object, with
1023
+ * null and undefined being returned as the empty string.
1024
+ *
1025
+ * @param {*} obj The object to convert.
1026
+ * @return {string} A string representation of the {@code obj}.
1027
+ */
1028
+ goog.string.makeSafe = function(obj) {
1029
+ return obj == null ? '' : String(obj);
1030
+ };
1031
+
1032
+
1033
+ /**
1034
+ * Concatenates string expressions. This is useful
1035
+ * since some browsers are very inefficient when it comes to using plus to
1036
+ * concat strings. Be careful when using null and undefined here since
1037
+ * these will not be included in the result. If you need to represent these
1038
+ * be sure to cast the argument to a String first.
1039
+ * For example:
1040
+ * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd'
1041
+ * buildString(null, undefined) -> ''
1042
+ * </pre>
1043
+ * @param {...*} var_args A list of strings to concatenate. If not a string,
1044
+ * it will be casted to one.
1045
+ * @return {string} The concatenation of {@code var_args}.
1046
+ */
1047
+ goog.string.buildString = function(var_args) {
1048
+ return Array.prototype.join.call(arguments, '');
1049
+ };
1050
+
1051
+
1052
+ /**
1053
+ * Returns a string with at least 64-bits of randomness.
1054
+ *
1055
+ * Doesn't trust Javascript's random function entirely. Uses a combination of
1056
+ * random and current timestamp, and then encodes the string in base-36 to
1057
+ * make it shorter.
1058
+ *
1059
+ * @return {string} A random string, e.g. sn1s7vb4gcic.
1060
+ */
1061
+ goog.string.getRandomString = function() {
1062
+ var x = 2147483648;
1063
+ return Math.floor(Math.random() * x).toString(36) +
1064
+ Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36);
1065
+ };
1066
+
1067
+
1068
+ /**
1069
+ * Compares two version numbers.
1070
+ *
1071
+ * @param {string|number} version1 Version of first item.
1072
+ * @param {string|number} version2 Version of second item.
1073
+ *
1074
+ * @return {number} 1 if {@code version1} is higher.
1075
+ * 0 if arguments are equal.
1076
+ * -1 if {@code version2} is higher.
1077
+ */
1078
+ goog.string.compareVersions = function(version1, version2) {
1079
+ var order = 0;
1080
+ // Trim leading and trailing whitespace and split the versions into
1081
+ // subversions.
1082
+ var v1Subs = goog.string.trim(String(version1)).split('.');
1083
+ var v2Subs = goog.string.trim(String(version2)).split('.');
1084
+ var subCount = Math.max(v1Subs.length, v2Subs.length);
1085
+
1086
+ // Iterate over the subversions, as long as they appear to be equivalent.
1087
+ for (var subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {
1088
+ var v1Sub = v1Subs[subIdx] || '';
1089
+ var v2Sub = v2Subs[subIdx] || '';
1090
+
1091
+ // Split the subversions into pairs of numbers and qualifiers (like 'b').
1092
+ // Two different RegExp objects are needed because they are both using
1093
+ // the 'g' flag.
1094
+ var v1CompParser = new RegExp('(\\d*)(\\D*)', 'g');
1095
+ var v2CompParser = new RegExp('(\\d*)(\\D*)', 'g');
1096
+ do {
1097
+ var v1Comp = v1CompParser.exec(v1Sub) || ['', '', ''];
1098
+ var v2Comp = v2CompParser.exec(v2Sub) || ['', '', ''];
1099
+ // Break if there are no more matches.
1100
+ if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {
1101
+ break;
1102
+ }
1103
+
1104
+ // Parse the numeric part of the subversion. A missing number is
1105
+ // equivalent to 0.
1106
+ var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);
1107
+ var v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);
1108
+
1109
+ // Compare the subversion components. The number has the highest
1110
+ // precedence. Next, if the numbers are equal, a subversion without any
1111
+ // qualifier is always higher than a subversion with any qualifier. Next,
1112
+ // the qualifiers are compared as strings.
1113
+ order = goog.string.compareElements_(v1CompNum, v2CompNum) ||
1114
+ goog.string.compareElements_(v1Comp[2].length == 0,
1115
+ v2Comp[2].length == 0) ||
1116
+ goog.string.compareElements_(v1Comp[2], v2Comp[2]);
1117
+ // Stop as soon as an inequality is discovered.
1118
+ } while (order == 0);
1119
+ }
1120
+
1121
+ return order;
1122
+ };
1123
+
1124
+
1125
+ /**
1126
+ * Compares elements of a version number.
1127
+ *
1128
+ * @param {string|number|boolean} left An element from a version number.
1129
+ * @param {string|number|boolean} right An element from a version number.
1130
+ *
1131
+ * @return {number} 1 if {@code left} is higher.
1132
+ * 0 if arguments are equal.
1133
+ * -1 if {@code right} is higher.
1134
+ * @private
1135
+ */
1136
+ goog.string.compareElements_ = function(left, right) {
1137
+ if (left < right) {
1138
+ return -1;
1139
+ } else if (left > right) {
1140
+ return 1;
1141
+ }
1142
+ return 0;
1143
+ };
1144
+
1145
+
1146
+ /**
1147
+ * Maximum value of #goog.string.hashCode, exclusive. 2^32.
1148
+ * @type {number}
1149
+ * @private
1150
+ */
1151
+ goog.string.HASHCODE_MAX_ = 0x100000000;
1152
+
1153
+
1154
+ /**
1155
+ * String hash function similar to java.lang.String.hashCode().
1156
+ * The hash code for a string is computed as
1157
+ * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
1158
+ * where s[i] is the ith character of the string and n is the length of
1159
+ * the string. We mod the result to make it between 0 (inclusive) and 2^32
1160
+ * (exclusive).
1161
+ * @param {string} str A string.
1162
+ * @return {number} Hash value for {@code str}, between 0 (inclusive) and 2^32
1163
+ * (exclusive). The empty string returns 0.
1164
+ */
1165
+ goog.string.hashCode = function(str) {
1166
+ var result = 0;
1167
+ for (var i = 0; i < str.length; ++i) {
1168
+ result = 31 * result + str.charCodeAt(i);
1169
+ // Normalize to 4 byte range, 0 ... 2^32.
1170
+ result %= goog.string.HASHCODE_MAX_;
1171
+ }
1172
+ return result;
1173
+ };
1174
+
1175
+
1176
+ /**
1177
+ * The most recent unique ID. |0 is equivalent to Math.floor in this case.
1178
+ * @type {number}
1179
+ * @private
1180
+ */
1181
+ goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0;
1182
+
1183
+
1184
+ /**
1185
+ * Generates and returns a string which is unique in the current document.
1186
+ * This is useful, for example, to create unique IDs for DOM elements.
1187
+ * @return {string} A unique id.
1188
+ */
1189
+ goog.string.createUniqueString = function() {
1190
+ return 'goog_' + goog.string.uniqueStringCounter_++;
1191
+ };
1192
+
1193
+
1194
+ /**
1195
+ * Converts the supplied string to a number, which may be Ininity or NaN.
1196
+ * This function strips whitespace: (toNumber(' 123') === 123)
1197
+ * This function accepts scientific notation: (toNumber('1e1') === 10)
1198
+ *
1199
+ * This is better than Javascript's built-in conversions because, sadly:
1200
+ * (Number(' ') === 0) and (parseFloat('123a') === 123)
1201
+ *
1202
+ * @param {string} str The string to convert.
1203
+ * @return {number} The number the supplied string represents, or NaN.
1204
+ */
1205
+ goog.string.toNumber = function(str) {
1206
+ var num = Number(str);
1207
+ if (num == 0 && goog.string.isEmpty(str)) {
1208
+ return NaN;
1209
+ }
1210
+ return num;
1211
+ };
1212
+
1213
+
1214
+ /**
1215
+ * A memoized cache for goog.string.toCamelCase.
1216
+ * @type {Object.<string>}
1217
+ * @private
1218
+ */
1219
+ goog.string.toCamelCaseCache_ = {};
1220
+
1221
+
1222
+ /**
1223
+ * Converts a string from selector-case to camelCase (e.g. from
1224
+ * "multi-part-string" to "multiPartString"), useful for converting
1225
+ * CSS selectors and HTML dataset keys to their equivalent JS properties.
1226
+ * @param {string} str The string in selector-case form.
1227
+ * @return {string} The string in camelCase form.
1228
+ */
1229
+ goog.string.toCamelCase = function(str) {
1230
+ return goog.string.toCamelCaseCache_[str] ||
1231
+ (goog.string.toCamelCaseCache_[str] =
1232
+ String(str).replace(/\-([a-z])/g, function(all, match) {
1233
+ return match.toUpperCase();
1234
+ }));
1235
+ };
1236
+
1237
+
1238
+ /**
1239
+ * A memoized cache for goog.string.toSelectorCase.
1240
+ * @type {Object.<string>}
1241
+ * @private
1242
+ */
1243
+ goog.string.toSelectorCaseCache_ = {};
1244
+
1245
+
1246
+ /**
1247
+ * Converts a string from camelCase to selector-case (e.g. from
1248
+ * "multiPartString" to "multi-part-string"), useful for converting JS
1249
+ * style and dataset properties to equivalent CSS selectors and HTML keys.
1250
+ * @param {string} str The string in camelCase form.
1251
+ * @return {string} The string in selector-case form.
1252
+ */
1253
+ goog.string.toSelectorCase = function(str) {
1254
+ return goog.string.toSelectorCaseCache_[str] ||
1255
+ (goog.string.toSelectorCaseCache_[str] =
1256
+ String(str).replace(/([A-Z])/g, '-$1').toLowerCase());
1257
+ };
1258
+ ;
1259
+ FI"
1260
+ F"%07fd243363de8f0ea5836de89804229a