| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2007 Apple Inc. All rights reserved. | 2 * Copyright (C) 2007 Apple Inc. All rights reserved. |
| 3 * Copyright (C) 2012 Google Inc. All rights reserved. | 3 * Copyright (C) 2012 Google Inc. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
| 7 * are met: | 7 * are met: |
| 8 * | 8 * |
| 9 * 1. Redistributions of source code must retain the above copyright | 9 * 1. Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 | 29 |
| 30 // FIXME: This performance optimization should be moved to blink so that all dev
elopers could enjoy it. | 30 // FIXME: This performance optimization should be moved to blink so that all dev
elopers could enjoy it. |
| 31 // console is retrieved with V8Window.getAttribute method which is slow. Here we
copy it to a js variable for faster access. | 31 // console is retrieved with V8Window.getAttribute method which is slow. Here we
copy it to a js variable for faster access. |
| 32 console = console; | 32 console = console; |
| 33 console.__originalAssert = console.assert; | 33 console.__originalAssert = console.assert; |
| 34 console.assert = function(value, message) | 34 console.assert = function(value, message) |
| 35 { | 35 { |
| 36 if (value) | 36 if (value) |
| 37 return; | 37 return; |
| 38 console.__originalAssert(value, message); | 38 console.__originalAssert(value, message); |
| 39 } | 39 }; |
| 40 | 40 |
| 41 /** @typedef {Array|NodeList|Arguments|{length: number}} */ | 41 /** @typedef {Array|NodeList|Arguments|{length: number}} */ |
| 42 var ArrayLike; | 42 var ArrayLike; |
| 43 | 43 |
| 44 /** | 44 /** |
| 45 * @param {number} m | 45 * @param {number} m |
| 46 * @param {number} n | 46 * @param {number} n |
| 47 * @return {number} | 47 * @return {number} |
| 48 */ | 48 */ |
| 49 function mod(m, n) | 49 function mod(m, n) |
| 50 { | 50 { |
| 51 return ((m % n) + n) % n; | 51 return ((m % n) + n) % n; |
| 52 } | 52 } |
| 53 | 53 |
| 54 /** | 54 /** |
| 55 * @param {string} string | 55 * @param {string} string |
| 56 * @return {!Array.<number>} | 56 * @return {!Array.<number>} |
| 57 */ | 57 */ |
| 58 String.prototype.findAll = function(string) | 58 String.prototype.findAll = function(string) |
| 59 { | 59 { |
| 60 var matches = []; | 60 var matches = []; |
| 61 var i = this.indexOf(string); | 61 var i = this.indexOf(string); |
| 62 while (i !== -1) { | 62 while (i !== -1) { |
| 63 matches.push(i); | 63 matches.push(i); |
| 64 i = this.indexOf(string, i + string.length); | 64 i = this.indexOf(string, i + string.length); |
| 65 } | 65 } |
| 66 return matches; | 66 return matches; |
| 67 } | 67 }; |
| 68 | 68 |
| 69 /** | 69 /** |
| 70 * @return {string} | 70 * @return {string} |
| 71 */ | 71 */ |
| 72 String.prototype.replaceControlCharacters = function() | 72 String.prototype.replaceControlCharacters = function() |
| 73 { | 73 { |
| 74 // Replace C0 and C1 control character sets with printable character. | 74 // Replace C0 and C1 control character sets with printable character. |
| 75 // Do not replace '\t', \n' and '\r'. | 75 // Do not replace '\t', \n' and '\r'. |
| 76 return this.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u0080-\u009f]/g
, "�"); | 76 return this.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u0080-\u009f]/g
, "�"); |
| 77 } | 77 }; |
| 78 | 78 |
| 79 /** | 79 /** |
| 80 * @return {boolean} | 80 * @return {boolean} |
| 81 */ | 81 */ |
| 82 String.prototype.isWhitespace = function() | 82 String.prototype.isWhitespace = function() |
| 83 { | 83 { |
| 84 return /^\s*$/.test(this); | 84 return /^\s*$/.test(this); |
| 85 } | 85 }; |
| 86 | 86 |
| 87 /** | 87 /** |
| 88 * @return {!Array.<number>} | 88 * @return {!Array.<number>} |
| 89 */ | 89 */ |
| 90 String.prototype.computeLineEndings = function() | 90 String.prototype.computeLineEndings = function() |
| 91 { | 91 { |
| 92 var endings = this.findAll("\n"); | 92 var endings = this.findAll("\n"); |
| 93 endings.push(this.length); | 93 endings.push(this.length); |
| 94 return endings; | 94 return endings; |
| 95 } | 95 }; |
| 96 | 96 |
| 97 /** | 97 /** |
| 98 * @param {string} chars | 98 * @param {string} chars |
| 99 * @return {string} | 99 * @return {string} |
| 100 */ | 100 */ |
| 101 String.prototype.escapeCharacters = function(chars) | 101 String.prototype.escapeCharacters = function(chars) |
| 102 { | 102 { |
| 103 var foundChar = false; | 103 var foundChar = false; |
| 104 for (var i = 0; i < chars.length; ++i) { | 104 for (var i = 0; i < chars.length; ++i) { |
| 105 if (this.indexOf(chars.charAt(i)) !== -1) { | 105 if (this.indexOf(chars.charAt(i)) !== -1) { |
| 106 foundChar = true; | 106 foundChar = true; |
| 107 break; | 107 break; |
| 108 } | 108 } |
| 109 } | 109 } |
| 110 | 110 |
| 111 if (!foundChar) | 111 if (!foundChar) |
| 112 return String(this); | 112 return String(this); |
| 113 | 113 |
| 114 var result = ""; | 114 var result = ""; |
| 115 for (var i = 0; i < this.length; ++i) { | 115 for (var i = 0; i < this.length; ++i) { |
| 116 if (chars.indexOf(this.charAt(i)) !== -1) | 116 if (chars.indexOf(this.charAt(i)) !== -1) |
| 117 result += "\\"; | 117 result += "\\"; |
| 118 result += this.charAt(i); | 118 result += this.charAt(i); |
| 119 } | 119 } |
| 120 | 120 |
| 121 return result; | 121 return result; |
| 122 } | 122 }; |
| 123 | 123 |
| 124 /** | 124 /** |
| 125 * @return {string} | 125 * @return {string} |
| 126 */ | 126 */ |
| 127 String.regexSpecialCharacters = function() | 127 String.regexSpecialCharacters = function() |
| 128 { | 128 { |
| 129 return "^[]{}()\\.^$*+?|-,"; | 129 return "^[]{}()\\.^$*+?|-,"; |
| 130 } | 130 }; |
| 131 | 131 |
| 132 /** | 132 /** |
| 133 * @return {string} | 133 * @return {string} |
| 134 */ | 134 */ |
| 135 String.prototype.escapeForRegExp = function() | 135 String.prototype.escapeForRegExp = function() |
| 136 { | 136 { |
| 137 return this.escapeCharacters(String.regexSpecialCharacters()); | 137 return this.escapeCharacters(String.regexSpecialCharacters()); |
| 138 } | 138 }; |
| 139 | 139 |
| 140 /** | 140 /** |
| 141 * @return {string} | 141 * @return {string} |
| 142 */ | 142 */ |
| 143 String.prototype.escapeHTML = function() | 143 String.prototype.escapeHTML = function() |
| 144 { | 144 { |
| 145 return this.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">
").replace(/"/g, """); // " doublequotes just for editor | 145 return this.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">
").replace(/"/g, """); // " doublequotes just for editor |
| 146 } | 146 }; |
| 147 | 147 |
| 148 /** | 148 /** |
| 149 * @return {string} | 149 * @return {string} |
| 150 */ | 150 */ |
| 151 String.prototype.unescapeHTML = function() | 151 String.prototype.unescapeHTML = function() |
| 152 { | 152 { |
| 153 return this.replace(/</g, "<") | 153 return this.replace(/</g, "<") |
| 154 .replace(/>/g, ">") | 154 .replace(/>/g, ">") |
| 155 .replace(/:/g, ":") | 155 .replace(/:/g, ":") |
| 156 .replace(/"/g, "\"") | 156 .replace(/"/g, "\"") |
| 157 .replace(/</g, "<") | 157 .replace(/</g, "<") |
| 158 .replace(/>/g, ">") | 158 .replace(/>/g, ">") |
| 159 .replace(/&/g, "&"); | 159 .replace(/&/g, "&"); |
| 160 } | 160 }; |
| 161 | 161 |
| 162 /** | 162 /** |
| 163 * @return {string} | 163 * @return {string} |
| 164 */ | 164 */ |
| 165 String.prototype.collapseWhitespace = function() | 165 String.prototype.collapseWhitespace = function() |
| 166 { | 166 { |
| 167 return this.replace(/[\s\xA0]+/g, " "); | 167 return this.replace(/[\s\xA0]+/g, " "); |
| 168 } | 168 }; |
| 169 | 169 |
| 170 /** | 170 /** |
| 171 * @param {number} maxLength | 171 * @param {number} maxLength |
| 172 * @return {string} | 172 * @return {string} |
| 173 */ | 173 */ |
| 174 String.prototype.trimMiddle = function(maxLength) | 174 String.prototype.trimMiddle = function(maxLength) |
| 175 { | 175 { |
| 176 if (this.length <= maxLength) | 176 if (this.length <= maxLength) |
| 177 return String(this); | 177 return String(this); |
| 178 var leftHalf = maxLength >> 1; | 178 var leftHalf = maxLength >> 1; |
| 179 var rightHalf = maxLength - leftHalf - 1; | 179 var rightHalf = maxLength - leftHalf - 1; |
| 180 return this.substr(0, leftHalf) + "\u2026" + this.substr(this.length - right
Half, rightHalf); | 180 return this.substr(0, leftHalf) + "\u2026" + this.substr(this.length - right
Half, rightHalf); |
| 181 } | 181 }; |
| 182 | 182 |
| 183 /** | 183 /** |
| 184 * @param {number} maxLength | 184 * @param {number} maxLength |
| 185 * @return {string} | 185 * @return {string} |
| 186 */ | 186 */ |
| 187 String.prototype.trimEnd = function(maxLength) | 187 String.prototype.trimEnd = function(maxLength) |
| 188 { | 188 { |
| 189 if (this.length <= maxLength) | 189 if (this.length <= maxLength) |
| 190 return String(this); | 190 return String(this); |
| 191 return this.substr(0, maxLength - 1) + "\u2026"; | 191 return this.substr(0, maxLength - 1) + "\u2026"; |
| 192 } | 192 }; |
| 193 | 193 |
| 194 /** | 194 /** |
| 195 * @param {?string=} baseURLDomain | 195 * @param {?string=} baseURLDomain |
| 196 * @return {string} | 196 * @return {string} |
| 197 */ | 197 */ |
| 198 String.prototype.trimURL = function(baseURLDomain) | 198 String.prototype.trimURL = function(baseURLDomain) |
| 199 { | 199 { |
| 200 var result = this.replace(/^(https|http|file):\/\//i, ""); | 200 var result = this.replace(/^(https|http|file):\/\//i, ""); |
| 201 if (baseURLDomain) { | 201 if (baseURLDomain) { |
| 202 if (result.toLowerCase().startsWith(baseURLDomain.toLowerCase())) | 202 if (result.toLowerCase().startsWith(baseURLDomain.toLowerCase())) |
| 203 result = result.substr(baseURLDomain.length); | 203 result = result.substr(baseURLDomain.length); |
| 204 } | 204 } |
| 205 return result; | 205 return result; |
| 206 } | 206 }; |
| 207 | 207 |
| 208 /** | 208 /** |
| 209 * @return {string} | 209 * @return {string} |
| 210 */ | 210 */ |
| 211 String.prototype.toTitleCase = function() | 211 String.prototype.toTitleCase = function() |
| 212 { | 212 { |
| 213 return this.substring(0, 1).toUpperCase() + this.substring(1); | 213 return this.substring(0, 1).toUpperCase() + this.substring(1); |
| 214 } | 214 }; |
| 215 | 215 |
| 216 /** | 216 /** |
| 217 * @param {string} other | 217 * @param {string} other |
| 218 * @return {number} | 218 * @return {number} |
| 219 */ | 219 */ |
| 220 String.prototype.compareTo = function(other) | 220 String.prototype.compareTo = function(other) |
| 221 { | 221 { |
| 222 if (this > other) | 222 if (this > other) |
| 223 return 1; | 223 return 1; |
| 224 if (this < other) | 224 if (this < other) |
| 225 return -1; | 225 return -1; |
| 226 return 0; | 226 return 0; |
| 227 } | 227 }; |
| 228 | 228 |
| 229 /** | 229 /** |
| 230 * @return {string} | 230 * @return {string} |
| 231 */ | 231 */ |
| 232 String.prototype.removeURLFragment = function() | 232 String.prototype.removeURLFragment = function() |
| 233 { | 233 { |
| 234 var fragmentIndex = this.indexOf("#"); | 234 var fragmentIndex = this.indexOf("#"); |
| 235 if (fragmentIndex === -1) | 235 if (fragmentIndex === -1) |
| 236 fragmentIndex = this.length; | 236 fragmentIndex = this.length; |
| 237 return this.substring(0, fragmentIndex); | 237 return this.substring(0, fragmentIndex); |
| 238 } | 238 }; |
| 239 | 239 |
| 240 /** | 240 /** |
| 241 * @param {string|undefined} string | 241 * @param {string|undefined} string |
| 242 * @return {number} | 242 * @return {number} |
| 243 */ | 243 */ |
| 244 String.hashCode = function(string) | 244 String.hashCode = function(string) |
| 245 { | 245 { |
| 246 if (!string) | 246 if (!string) |
| 247 return 0; | 247 return 0; |
| 248 // Hash algorithm for substrings is described in "Über die Komplexität der M
ultiplikation in | 248 // Hash algorithm for substrings is described in "Über die Komplexität der M
ultiplikation in |
| 249 // eingeschränkten Branchingprogrammmodellen" by Woelfe. | 249 // eingeschränkten Branchingprogrammmodellen" by Woelfe. |
| 250 // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#
SECTION00832000000000000000 | 250 // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#
SECTION00832000000000000000 |
| 251 var p = ((1 << 30) * 4 - 5); // prime: 2^32 - 5 | 251 var p = ((1 << 30) * 4 - 5); // prime: 2^32 - 5 |
| 252 var z = 0x5033d967; // 32 bits from random.org | 252 var z = 0x5033d967; // 32 bits from random.org |
| 253 var z2 = 0x59d2f15d; // random odd 32 bit number | 253 var z2 = 0x59d2f15d; // random odd 32 bit number |
| 254 var s = 0; | 254 var s = 0; |
| 255 var zi = 1; | 255 var zi = 1; |
| 256 for (var i = 0; i < string.length; i++) { | 256 for (var i = 0; i < string.length; i++) { |
| 257 var xi = string.charCodeAt(i) * z2; | 257 var xi = string.charCodeAt(i) * z2; |
| 258 s = (s + zi * xi) % p; | 258 s = (s + zi * xi) % p; |
| 259 zi = (zi * z) % p; | 259 zi = (zi * z) % p; |
| 260 } | 260 } |
| 261 s = (s + zi * (p - 1)) % p; | 261 s = (s + zi * (p - 1)) % p; |
| 262 return Math.abs(s | 0); | 262 return Math.abs(s | 0); |
| 263 } | 263 }; |
| 264 | 264 |
| 265 /** | 265 /** |
| 266 * @param {string} string | 266 * @param {string} string |
| 267 * @param {number} index | 267 * @param {number} index |
| 268 * @return {boolean} | 268 * @return {boolean} |
| 269 */ | 269 */ |
| 270 String.isDigitAt = function(string, index) | 270 String.isDigitAt = function(string, index) |
| 271 { | 271 { |
| 272 var c = string.charCodeAt(index); | 272 var c = string.charCodeAt(index); |
| 273 return (48 <= c && c <= 57); | 273 return (48 <= c && c <= 57); |
| 274 } | 274 }; |
| 275 | 275 |
| 276 /** | 276 /** |
| 277 * @return {string} | 277 * @return {string} |
| 278 */ | 278 */ |
| 279 String.prototype.toBase64 = function() | 279 String.prototype.toBase64 = function() |
| 280 { | 280 { |
| 281 /** | 281 /** |
| 282 * @param {number} b | 282 * @param {number} b |
| 283 * @return {number} | 283 * @return {number} |
| 284 */ | 284 */ |
| (...skipping 15 matching lines...) Expand all Loading... |
| 300 if (shift === 2) { | 300 if (shift === 2) { |
| 301 encoded += String.fromCharCode(encodeBits(v >>> 18 & 63), encodeBits
(v >>> 12 & 63), encodeBits(v >>> 6 & 63), encodeBits(v & 63)); | 301 encoded += String.fromCharCode(encodeBits(v >>> 18 & 63), encodeBits
(v >>> 12 & 63), encodeBits(v >>> 6 & 63), encodeBits(v & 63)); |
| 302 v = 0; | 302 v = 0; |
| 303 } | 303 } |
| 304 } | 304 } |
| 305 if (shift === 0) | 305 if (shift === 0) |
| 306 encoded += String.fromCharCode(encodeBits(v >>> 18 & 63), encodeBits(v >
>> 12 & 63), 61, 61); | 306 encoded += String.fromCharCode(encodeBits(v >>> 18 & 63), encodeBits(v >
>> 12 & 63), 61, 61); |
| 307 else if (shift === 1) | 307 else if (shift === 1) |
| 308 encoded += String.fromCharCode(encodeBits(v >>> 18 & 63), encodeBits(v >
>> 12 & 63), encodeBits(v >>> 6 & 63), 61); | 308 encoded += String.fromCharCode(encodeBits(v >>> 18 & 63), encodeBits(v >
>> 12 & 63), encodeBits(v >>> 6 & 63), 61); |
| 309 return encoded; | 309 return encoded; |
| 310 } | 310 }; |
| 311 | 311 |
| 312 /** | 312 /** |
| 313 * @param {string} a | 313 * @param {string} a |
| 314 * @param {string} b | 314 * @param {string} b |
| 315 * @return {number} | 315 * @return {number} |
| 316 */ | 316 */ |
| 317 String.naturalOrderComparator = function(a, b) | 317 String.naturalOrderComparator = function(a, b) |
| 318 { | 318 { |
| 319 var chunk = /^\d+|^\D+/; | 319 var chunk = /^\d+|^\D+/; |
| 320 var chunka, chunkb, anum, bnum; | 320 var chunka, chunkb, anum, bnum; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 344 if (!+chunka && !+chunkb) // chunks are strings of all 0s (speci
al case) | 344 if (!+chunka && !+chunkb) // chunks are strings of all 0s (speci
al case) |
| 345 return chunka.length - chunkb.length; | 345 return chunka.length - chunkb.length; |
| 346 else | 346 else |
| 347 return chunkb.length - chunka.length; | 347 return chunkb.length - chunka.length; |
| 348 } | 348 } |
| 349 } else if (chunka !== chunkb) | 349 } else if (chunka !== chunkb) |
| 350 return (chunka < chunkb) ? -1 : 1; | 350 return (chunka < chunkb) ? -1 : 1; |
| 351 a = a.substring(chunka.length); | 351 a = a.substring(chunka.length); |
| 352 b = b.substring(chunkb.length); | 352 b = b.substring(chunkb.length); |
| 353 } | 353 } |
| 354 } | 354 }; |
| 355 | 355 |
| 356 /** | 356 /** |
| 357 * @param {string} a | 357 * @param {string} a |
| 358 * @param {string} b | 358 * @param {string} b |
| 359 * @return {number} | 359 * @return {number} |
| 360 */ | 360 */ |
| 361 String.caseInsensetiveComparator = function(a, b) | 361 String.caseInsensetiveComparator = function(a, b) |
| 362 { | 362 { |
| 363 a = a.toUpperCase(); | 363 a = a.toUpperCase(); |
| 364 b = b.toUpperCase(); | 364 b = b.toUpperCase(); |
| 365 if (a === b) | 365 if (a === b) |
| 366 return 0; | 366 return 0; |
| 367 return a > b ? 1 : -1; | 367 return a > b ? 1 : -1; |
| 368 } | 368 }; |
| 369 | 369 |
| 370 /** | 370 /** |
| 371 * @param {number} num | 371 * @param {number} num |
| 372 * @param {number} min | 372 * @param {number} min |
| 373 * @param {number} max | 373 * @param {number} max |
| 374 * @return {number} | 374 * @return {number} |
| 375 */ | 375 */ |
| 376 Number.constrain = function(num, min, max) | 376 Number.constrain = function(num, min, max) |
| 377 { | 377 { |
| 378 if (num < min) | 378 if (num < min) |
| 379 num = min; | 379 num = min; |
| 380 else if (num > max) | 380 else if (num > max) |
| 381 num = max; | 381 num = max; |
| 382 return num; | 382 return num; |
| 383 } | 383 }; |
| 384 | 384 |
| 385 /** | 385 /** |
| 386 * @param {number} a | 386 * @param {number} a |
| 387 * @param {number} b | 387 * @param {number} b |
| 388 * @return {number} | 388 * @return {number} |
| 389 */ | 389 */ |
| 390 Number.gcd = function(a, b) | 390 Number.gcd = function(a, b) |
| 391 { | 391 { |
| 392 if (b === 0) | 392 if (b === 0) |
| 393 return a; | 393 return a; |
| 394 else | 394 else |
| 395 return Number.gcd(b, a % b); | 395 return Number.gcd(b, a % b); |
| 396 } | 396 }; |
| 397 | 397 |
| 398 /** | 398 /** |
| 399 * @param {string} value | 399 * @param {string} value |
| 400 * @return {string} | 400 * @return {string} |
| 401 */ | 401 */ |
| 402 Number.toFixedIfFloating = function(value) | 402 Number.toFixedIfFloating = function(value) |
| 403 { | 403 { |
| 404 if (!value || isNaN(value)) | 404 if (!value || isNaN(value)) |
| 405 return value; | 405 return value; |
| 406 var number = Number(value); | 406 var number = Number(value); |
| 407 return number % 1 ? number.toFixed(3) : String(number); | 407 return number % 1 ? number.toFixed(3) : String(number); |
| 408 } | 408 }; |
| 409 | 409 |
| 410 /** | 410 /** |
| 411 * @return {boolean} | 411 * @return {boolean} |
| 412 */ | 412 */ |
| 413 Date.prototype.isValid = function() | 413 Date.prototype.isValid = function() |
| 414 { | 414 { |
| 415 return !isNaN(this.getTime()) | 415 return !isNaN(this.getTime()); |
| 416 } | 416 }; |
| 417 | 417 |
| 418 /** | 418 /** |
| 419 * @return {string} | 419 * @return {string} |
| 420 */ | 420 */ |
| 421 Date.prototype.toISO8601Compact = function() | 421 Date.prototype.toISO8601Compact = function() |
| 422 { | 422 { |
| 423 /** | 423 /** |
| 424 * @param {number} x | 424 * @param {number} x |
| 425 * @return {string} | 425 * @return {string} |
| 426 */ | 426 */ |
| 427 function leadZero(x) | 427 function leadZero(x) |
| 428 { | 428 { |
| 429 return (x > 9 ? "" : "0") + x; | 429 return (x > 9 ? "" : "0") + x; |
| 430 } | 430 } |
| 431 return this.getFullYear() + | 431 return this.getFullYear() + |
| 432 leadZero(this.getMonth() + 1) + | 432 leadZero(this.getMonth() + 1) + |
| 433 leadZero(this.getDate()) + "T" + | 433 leadZero(this.getDate()) + "T" + |
| 434 leadZero(this.getHours()) + | 434 leadZero(this.getHours()) + |
| 435 leadZero(this.getMinutes()) + | 435 leadZero(this.getMinutes()) + |
| 436 leadZero(this.getSeconds()); | 436 leadZero(this.getSeconds()); |
| 437 } | 437 }; |
| 438 | 438 |
| 439 /** | 439 /** |
| 440 * @return {string} | 440 * @return {string} |
| 441 */ | 441 */ |
| 442 Date.prototype.toConsoleTime = function() | 442 Date.prototype.toConsoleTime = function() |
| 443 { | 443 { |
| 444 /** | 444 /** |
| 445 * @param {number} x | 445 * @param {number} x |
| 446 * @return {string} | 446 * @return {string} |
| 447 */ | 447 */ |
| (...skipping 11 matching lines...) Expand all Loading... |
| 459 return "0".repeat(3 - x.toString().length) + x; | 459 return "0".repeat(3 - x.toString().length) + x; |
| 460 } | 460 } |
| 461 | 461 |
| 462 return this.getFullYear() + "-" + | 462 return this.getFullYear() + "-" + |
| 463 leadZero2(this.getMonth() + 1) + "-" + | 463 leadZero2(this.getMonth() + 1) + "-" + |
| 464 leadZero2(this.getDate()) + " " + | 464 leadZero2(this.getDate()) + " " + |
| 465 leadZero2(this.getHours()) + ":" + | 465 leadZero2(this.getHours()) + ":" + |
| 466 leadZero2(this.getMinutes()) + ":" + | 466 leadZero2(this.getMinutes()) + ":" + |
| 467 leadZero2(this.getSeconds()) + "." + | 467 leadZero2(this.getSeconds()) + "." + |
| 468 leadZero3(this.getMilliseconds()); | 468 leadZero3(this.getMilliseconds()); |
| 469 } | 469 }; |
| 470 | 470 |
| 471 Object.defineProperty(Array.prototype, "remove", { | 471 Object.defineProperty(Array.prototype, "remove", { |
| 472 /** | 472 /** |
| 473 * @param {!T} value | 473 * @param {!T} value |
| 474 * @param {boolean=} firstOnly | 474 * @param {boolean=} firstOnly |
| 475 * @return {boolean} | 475 * @return {boolean} |
| 476 * @this {Array.<!T>} | 476 * @this {Array.<!T>} |
| 477 * @template T | 477 * @template T |
| 478 */ | 478 */ |
| 479 value: function(value, firstOnly) | 479 value: function(value, firstOnly) |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 603 quickSortRange(array, comparator, left, pivotNewIndex - 1, s
ortWindowLeft, sortWindowRight); | 603 quickSortRange(array, comparator, left, pivotNewIndex - 1, s
ortWindowLeft, sortWindowRight); |
| 604 if (pivotNewIndex < sortWindowRight) | 604 if (pivotNewIndex < sortWindowRight) |
| 605 quickSortRange(array, comparator, pivotNewIndex + 1, right,
sortWindowLeft, sortWindowRight); | 605 quickSortRange(array, comparator, pivotNewIndex + 1, right,
sortWindowLeft, sortWindowRight); |
| 606 } | 606 } |
| 607 if (leftBound === 0 && rightBound === (this.length - 1) && sortWindo
wLeft === 0 && sortWindowRight >= rightBound) | 607 if (leftBound === 0 && rightBound === (this.length - 1) && sortWindo
wLeft === 0 && sortWindowRight >= rightBound) |
| 608 this.sort(comparator); | 608 this.sort(comparator); |
| 609 else | 609 else |
| 610 quickSortRange(this, comparator, leftBound, rightBound, sortWind
owLeft, sortWindowRight); | 610 quickSortRange(this, comparator, leftBound, rightBound, sortWind
owLeft, sortWindowRight); |
| 611 return this; | 611 return this; |
| 612 } | 612 } |
| 613 } | 613 }; |
| 614 Object.defineProperty(Array.prototype, "sortRange", sortRange); | 614 Object.defineProperty(Array.prototype, "sortRange", sortRange); |
| 615 Object.defineProperty(Uint32Array.prototype, "sortRange", sortRange); | 615 Object.defineProperty(Uint32Array.prototype, "sortRange", sortRange); |
| 616 })(); | 616 })(); |
| 617 | 617 |
| 618 Object.defineProperty(Array.prototype, "stableSort", { | 618 Object.defineProperty(Array.prototype, "stableSort", { |
| 619 /** | 619 /** |
| 620 * @param {function(?T, ?T): number=} comparator | 620 * @param {function(?T, ?T): number=} comparator |
| 621 * @return {!Array.<?T>} | 621 * @return {!Array.<?T>} |
| 622 * @this {Array.<?T>} | 622 * @this {Array.<?T>} |
| 623 * @template T | 623 * @template T |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 672 * @param {number} k | 672 * @param {number} k |
| 673 * @param {function(number, number): number=} comparator | 673 * @param {function(number, number): number=} comparator |
| 674 * @return {number|undefined} | 674 * @return {number|undefined} |
| 675 * @this {Array.<number>} | 675 * @this {Array.<number>} |
| 676 */ | 676 */ |
| 677 value: function(k, comparator) | 677 value: function(k, comparator) |
| 678 { | 678 { |
| 679 if (k < 0 || k >= this.length) | 679 if (k < 0 || k >= this.length) |
| 680 return; | 680 return; |
| 681 if (!comparator) | 681 if (!comparator) |
| 682 comparator = function(a, b) { return a - b; } | 682 comparator = function(a, b) { return a - b; }; |
| 683 | 683 |
| 684 var low = 0; | 684 var low = 0; |
| 685 var high = this.length - 1; | 685 var high = this.length - 1; |
| 686 for (;;) { | 686 for (;;) { |
| 687 var pivotPosition = this.partition(comparator, low, high, Math.floor
((high + low) / 2)); | 687 var pivotPosition = this.partition(comparator, low, high, Math.floor
((high + low) / 2)); |
| 688 if (pivotPosition === k) | 688 if (pivotPosition === k) |
| 689 return this[k]; | 689 return this[k]; |
| 690 else if (pivotPosition > k) | 690 else if (pivotPosition > k) |
| 691 high = pivotPosition - 1; | 691 high = pivotPosition - 1; |
| 692 else | 692 else |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 886 })(); | 886 })(); |
| 887 | 887 |
| 888 /** | 888 /** |
| 889 * @param {string} format | 889 * @param {string} format |
| 890 * @param {...*} var_arg | 890 * @param {...*} var_arg |
| 891 * @return {string} | 891 * @return {string} |
| 892 */ | 892 */ |
| 893 String.sprintf = function(format, var_arg) | 893 String.sprintf = function(format, var_arg) |
| 894 { | 894 { |
| 895 return String.vsprintf(format, Array.prototype.slice.call(arguments, 1)); | 895 return String.vsprintf(format, Array.prototype.slice.call(arguments, 1)); |
| 896 } | 896 }; |
| 897 | 897 |
| 898 /** | 898 /** |
| 899 * @param {string} format | 899 * @param {string} format |
| 900 * @param {!Object.<string, function(string, ...):*>} formatters | 900 * @param {!Object.<string, function(string, ...):*>} formatters |
| 901 * @return {!Array.<!Object>} | 901 * @return {!Array.<!Object>} |
| 902 */ | 902 */ |
| 903 String.tokenizeFormatString = function(format, formatters) | 903 String.tokenizeFormatString = function(format, formatters) |
| 904 { | 904 { |
| 905 var tokens = []; | 905 var tokens = []; |
| 906 var substitutionIndex = 0; | 906 var substitutionIndex = 0; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 967 | 967 |
| 968 addSpecifierToken(format[index], precision, substitutionIndex); | 968 addSpecifierToken(format[index], precision, substitutionIndex); |
| 969 | 969 |
| 970 ++substitutionIndex; | 970 ++substitutionIndex; |
| 971 ++index; | 971 ++index; |
| 972 } | 972 } |
| 973 | 973 |
| 974 addStringToken(format.substring(index)); | 974 addStringToken(format.substring(index)); |
| 975 | 975 |
| 976 return tokens; | 976 return tokens; |
| 977 } | 977 }; |
| 978 | 978 |
| 979 String.standardFormatters = { | 979 String.standardFormatters = { |
| 980 /** | 980 /** |
| 981 * @return {number} | 981 * @return {number} |
| 982 */ | 982 */ |
| 983 d: function(substitution) | 983 d: function(substitution) |
| 984 { | 984 { |
| 985 return !isNaN(substitution) ? substitution : 0; | 985 return !isNaN(substitution) ? substitution : 0; |
| 986 }, | 986 }, |
| 987 | 987 |
| 988 /** | 988 /** |
| 989 * @return {number} | 989 * @return {number} |
| 990 */ | 990 */ |
| 991 f: function(substitution, token) | 991 f: function(substitution, token) |
| 992 { | 992 { |
| 993 if (substitution && token.precision > -1) | 993 if (substitution && token.precision > -1) |
| 994 substitution = substitution.toFixed(token.precision); | 994 substitution = substitution.toFixed(token.precision); |
| 995 return !isNaN(substitution) ? substitution : (token.precision > -1 ? Num
ber(0).toFixed(token.precision) : 0); | 995 return !isNaN(substitution) ? substitution : (token.precision > -1 ? Num
ber(0).toFixed(token.precision) : 0); |
| 996 }, | 996 }, |
| 997 | 997 |
| 998 /** | 998 /** |
| 999 * @return {string} | 999 * @return {string} |
| 1000 */ | 1000 */ |
| 1001 s: function(substitution) | 1001 s: function(substitution) |
| 1002 { | 1002 { |
| 1003 return substitution; | 1003 return substitution; |
| 1004 } | 1004 } |
| 1005 } | 1005 }; |
| 1006 | 1006 |
| 1007 /** | 1007 /** |
| 1008 * @param {string} format | 1008 * @param {string} format |
| 1009 * @param {!Array.<*>} substitutions | 1009 * @param {!Array.<*>} substitutions |
| 1010 * @return {string} | 1010 * @return {string} |
| 1011 */ | 1011 */ |
| 1012 String.vsprintf = function(format, substitutions) | 1012 String.vsprintf = function(format, substitutions) |
| 1013 { | 1013 { |
| 1014 return String.format(format, substitutions, String.standardFormatters, "", f
unction(a, b) { return a + b; }).formattedResult; | 1014 return String.format(format, substitutions, String.standardFormatters, "", f
unction(a, b) { return a + b; }).formattedResult; |
| 1015 } | 1015 }; |
| 1016 | 1016 |
| 1017 /** | 1017 /** |
| 1018 * @param {string} format | 1018 * @param {string} format |
| 1019 * @param {?ArrayLike} substitutions | 1019 * @param {?ArrayLike} substitutions |
| 1020 * @param {!Object.<string, function(string, ...):Q>} formatters | 1020 * @param {!Object.<string, function(string, ...):Q>} formatters |
| 1021 * @param {!T} initialValue | 1021 * @param {!T} initialValue |
| 1022 * @param {function(T, Q): T|undefined} append | 1022 * @param {function(T, Q): T|undefined} append |
| 1023 * @param {!Array.<!Object>=} tokenizedFormat | 1023 * @param {!Array.<!Object>=} tokenizedFormat |
| 1024 * @return {!{formattedResult: T, unusedSubstitutions: ?ArrayLike}}; | 1024 * @return {!{formattedResult: T, unusedSubstitutions: ?ArrayLike}}; |
| 1025 * @template T, Q | 1025 * @template T, Q |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1082 } | 1082 } |
| 1083 | 1083 |
| 1084 var unusedSubstitutions = []; | 1084 var unusedSubstitutions = []; |
| 1085 for (var i = 0; i < substitutions.length; ++i) { | 1085 for (var i = 0; i < substitutions.length; ++i) { |
| 1086 if (i in usedSubstitutionIndexes) | 1086 if (i in usedSubstitutionIndexes) |
| 1087 continue; | 1087 continue; |
| 1088 unusedSubstitutions.push(substitutions[i]); | 1088 unusedSubstitutions.push(substitutions[i]); |
| 1089 } | 1089 } |
| 1090 | 1090 |
| 1091 return { formattedResult: result, unusedSubstitutions: unusedSubstitutions }
; | 1091 return { formattedResult: result, unusedSubstitutions: unusedSubstitutions }
; |
| 1092 } | 1092 }; |
| 1093 | 1093 |
| 1094 /** | 1094 /** |
| 1095 * @param {string} query | 1095 * @param {string} query |
| 1096 * @param {boolean} caseSensitive | 1096 * @param {boolean} caseSensitive |
| 1097 * @param {boolean} isRegex | 1097 * @param {boolean} isRegex |
| 1098 * @return {!RegExp} | 1098 * @return {!RegExp} |
| 1099 */ | 1099 */ |
| 1100 function createSearchRegex(query, caseSensitive, isRegex) | 1100 function createSearchRegex(query, caseSensitive, isRegex) |
| 1101 { | 1101 { |
| 1102 var regexFlags = caseSensitive ? "g" : "gi"; | 1102 var regexFlags = caseSensitive ? "g" : "gi"; |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1174 return spacesPadding(paddingLength) + numberString; | 1174 return spacesPadding(paddingLength) + numberString; |
| 1175 } | 1175 } |
| 1176 | 1176 |
| 1177 /** | 1177 /** |
| 1178 * @return {!Array.<T>} | 1178 * @return {!Array.<T>} |
| 1179 * @template T | 1179 * @template T |
| 1180 */ | 1180 */ |
| 1181 Set.prototype.valuesArray = function() | 1181 Set.prototype.valuesArray = function() |
| 1182 { | 1182 { |
| 1183 return Array.from(this.values()); | 1183 return Array.from(this.values()); |
| 1184 } | 1184 }; |
| 1185 | 1185 |
| 1186 /** | 1186 /** |
| 1187 * @param {!Iterable<T>|!Array<!T>} iterable | 1187 * @param {!Iterable<T>|!Array<!T>} iterable |
| 1188 * @template T | 1188 * @template T |
| 1189 */ | 1189 */ |
| 1190 Set.prototype.addAll = function(iterable) | 1190 Set.prototype.addAll = function(iterable) |
| 1191 { | 1191 { |
| 1192 for (var e of iterable) | 1192 for (var e of iterable) |
| 1193 this.add(e); | 1193 this.add(e); |
| 1194 } | 1194 }; |
| 1195 | 1195 |
| 1196 /** | 1196 /** |
| 1197 * @param {!Iterable<T>|!Array<!T>} iterable | 1197 * @param {!Iterable<T>|!Array<!T>} iterable |
| 1198 * @return {boolean} | 1198 * @return {boolean} |
| 1199 * @template T | 1199 * @template T |
| 1200 */ | 1200 */ |
| 1201 Set.prototype.containsAll = function(iterable) | 1201 Set.prototype.containsAll = function(iterable) |
| 1202 { | 1202 { |
| 1203 for (var e of iterable) { | 1203 for (var e of iterable) { |
| 1204 if (!this.has(e)) | 1204 if (!this.has(e)) |
| 1205 return false; | 1205 return false; |
| 1206 } | 1206 } |
| 1207 return true; | 1207 return true; |
| 1208 } | 1208 }; |
| 1209 | 1209 |
| 1210 /** | 1210 /** |
| 1211 * @return {T} | 1211 * @return {T} |
| 1212 * @template T | 1212 * @template T |
| 1213 */ | 1213 */ |
| 1214 Map.prototype.remove = function(key) | 1214 Map.prototype.remove = function(key) |
| 1215 { | 1215 { |
| 1216 var value = this.get(key); | 1216 var value = this.get(key); |
| 1217 this.delete(key); | 1217 this.delete(key); |
| 1218 return value; | 1218 return value; |
| 1219 } | 1219 }; |
| 1220 | 1220 |
| 1221 /** | 1221 /** |
| 1222 * @return {!Array<!VALUE>} | 1222 * @return {!Array<!VALUE>} |
| 1223 */ | 1223 */ |
| 1224 Map.prototype.valuesArray = function() | 1224 Map.prototype.valuesArray = function() |
| 1225 { | 1225 { |
| 1226 return Array.from(this.values()); | 1226 return Array.from(this.values()); |
| 1227 } | 1227 }; |
| 1228 | 1228 |
| 1229 /** | 1229 /** |
| 1230 * @return {!Array<!KEY>} | 1230 * @return {!Array<!KEY>} |
| 1231 */ | 1231 */ |
| 1232 Map.prototype.keysArray = function() | 1232 Map.prototype.keysArray = function() |
| 1233 { | 1233 { |
| 1234 return Array.from(this.keys()); | 1234 return Array.from(this.keys()); |
| 1235 } | 1235 }; |
| 1236 | 1236 |
| 1237 /** | 1237 /** |
| 1238 * @return {!Multimap<!KEY, !VALUE>} | 1238 * @return {!Multimap<!KEY, !VALUE>} |
| 1239 */ | 1239 */ |
| 1240 Map.prototype.inverse = function() | 1240 Map.prototype.inverse = function() |
| 1241 { | 1241 { |
| 1242 var result = new Multimap(); | 1242 var result = new Multimap(); |
| 1243 for (var key of this.keys()) { | 1243 for (var key of this.keys()) { |
| 1244 var value = this.get(key); | 1244 var value = this.get(key); |
| 1245 result.set(value, key); | 1245 result.set(value, key); |
| 1246 } | 1246 } |
| 1247 return result; | 1247 return result; |
| 1248 } | 1248 }; |
| 1249 | 1249 |
| 1250 /** | 1250 /** |
| 1251 * @constructor | 1251 * @constructor |
| 1252 * @template K, V | 1252 * @template K, V |
| 1253 */ | 1253 */ |
| 1254 var Multimap = function() | 1254 var Multimap = function() |
| 1255 { | 1255 { |
| 1256 /** @type {!Map.<K, !Set.<!V>>} */ | 1256 /** @type {!Map.<K, !Set.<!V>>} */ |
| 1257 this._map = new Map(); | 1257 this._map = new Map(); |
| 1258 } | 1258 }; |
| 1259 | 1259 |
| 1260 Multimap.prototype = { | 1260 Multimap.prototype = { |
| 1261 /** | 1261 /** |
| 1262 * @param {K} key | 1262 * @param {K} key |
| 1263 * @param {V} value | 1263 * @param {V} value |
| 1264 */ | 1264 */ |
| 1265 set: function(key, value) | 1265 set: function(key, value) |
| 1266 { | 1266 { |
| 1267 var set = this._map.get(key); | 1267 var set = this._map.get(key); |
| 1268 if (!set) { | 1268 if (!set) { |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1351 var keys = this.keysArray(); | 1351 var keys = this.keysArray(); |
| 1352 for (var i = 0; i < keys.length; ++i) | 1352 for (var i = 0; i < keys.length; ++i) |
| 1353 result.pushAll(this.get(keys[i]).valuesArray()); | 1353 result.pushAll(this.get(keys[i]).valuesArray()); |
| 1354 return result; | 1354 return result; |
| 1355 }, | 1355 }, |
| 1356 | 1356 |
| 1357 clear: function() | 1357 clear: function() |
| 1358 { | 1358 { |
| 1359 this._map.clear(); | 1359 this._map.clear(); |
| 1360 } | 1360 } |
| 1361 } | 1361 }; |
| 1362 | 1362 |
| 1363 /** | 1363 /** |
| 1364 * @param {string} url | 1364 * @param {string} url |
| 1365 * @return {!Promise.<string>} | 1365 * @return {!Promise.<string>} |
| 1366 */ | 1366 */ |
| 1367 function loadXHR(url) | 1367 function loadXHR(url) |
| 1368 { | 1368 { |
| 1369 return new Promise(load); | 1369 return new Promise(load); |
| 1370 | 1370 |
| 1371 function load(successCallback, failureCallback) | 1371 function load(successCallback, failureCallback) |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1445 _incomingCallback: function(userCallback) | 1445 _incomingCallback: function(userCallback) |
| 1446 { | 1446 { |
| 1447 console.assert(this._pendingIncomingCallbacksCount > 0); | 1447 console.assert(this._pendingIncomingCallbacksCount > 0); |
| 1448 if (userCallback) { | 1448 if (userCallback) { |
| 1449 var args = Array.prototype.slice.call(arguments, 1); | 1449 var args = Array.prototype.slice.call(arguments, 1); |
| 1450 userCallback.apply(null, args); | 1450 userCallback.apply(null, args); |
| 1451 } | 1451 } |
| 1452 if (!--this._pendingIncomingCallbacksCount && this._outgoingCallback) | 1452 if (!--this._pendingIncomingCallbacksCount && this._outgoingCallback) |
| 1453 this._outgoingCallback(); | 1453 this._outgoingCallback(); |
| 1454 } | 1454 } |
| 1455 } | 1455 }; |
| 1456 | 1456 |
| 1457 /** | 1457 /** |
| 1458 * @param {*} value | 1458 * @param {*} value |
| 1459 */ | 1459 */ |
| 1460 function suppressUnused(value) | 1460 function suppressUnused(value) |
| 1461 { | 1461 { |
| 1462 } | 1462 } |
| 1463 | 1463 |
| 1464 /** | 1464 /** |
| 1465 * @param {function()} callback | 1465 * @param {function()} callback |
| 1466 * @return {number} | 1466 * @return {number} |
| 1467 */ | 1467 */ |
| 1468 self.setImmediate = function(callback) | 1468 self.setImmediate = function(callback) |
| 1469 { | 1469 { |
| 1470 Promise.resolve().then(callback); | 1470 Promise.resolve().then(callback); |
| 1471 return 0; | 1471 return 0; |
| 1472 } | 1472 }; |
| 1473 | 1473 |
| 1474 /** | 1474 /** |
| 1475 * @param {function(...?)} callback | 1475 * @param {function(...?)} callback |
| 1476 * @return {!Promise.<T>} | 1476 * @return {!Promise.<T>} |
| 1477 * @template T | 1477 * @template T |
| 1478 */ | 1478 */ |
| 1479 Promise.prototype.spread = function(callback) | 1479 Promise.prototype.spread = function(callback) |
| 1480 { | 1480 { |
| 1481 return this.then(spreadPromise); | 1481 return this.then(spreadPromise); |
| 1482 | 1482 |
| 1483 function spreadPromise(arg) | 1483 function spreadPromise(arg) |
| 1484 { | 1484 { |
| 1485 return callback.apply(null, arg); | 1485 return callback.apply(null, arg); |
| 1486 } | 1486 } |
| 1487 } | 1487 }; |
| 1488 | 1488 |
| 1489 /** | 1489 /** |
| 1490 * @param {T} defaultValue | 1490 * @param {T} defaultValue |
| 1491 * @return {!Promise.<T>} | 1491 * @return {!Promise.<T>} |
| 1492 * @template T | 1492 * @template T |
| 1493 */ | 1493 */ |
| 1494 Promise.prototype.catchException = function(defaultValue) { | 1494 Promise.prototype.catchException = function(defaultValue) { |
| 1495 return this.catch(function(error) { | 1495 return this.catch(function(error) { |
| 1496 console.error(error); | 1496 console.error(error); |
| 1497 return defaultValue; | 1497 return defaultValue; |
| 1498 }); | 1498 }); |
| 1499 } | 1499 }; |
| 1500 | 1500 |
| 1501 /** | 1501 /** |
| 1502 * @param {!Map<number, ?>} other | 1502 * @param {!Map<number, ?>} other |
| 1503 * @param {function(!VALUE,?):boolean} isEqual | 1503 * @param {function(!VALUE,?):boolean} isEqual |
| 1504 * @return {!{removed: !Array<!VALUE>, added: !Array<?>, equal: !Array<!VALUE>}} | 1504 * @return {!{removed: !Array<!VALUE>, added: !Array<?>, equal: !Array<!VALUE>}} |
| 1505 * @this {Map<number, VALUE>} | 1505 * @this {Map<number, VALUE>} |
| 1506 */ | 1506 */ |
| 1507 Map.prototype.diff = function(other, isEqual) | 1507 Map.prototype.diff = function(other, isEqual) |
| 1508 { | 1508 { |
| 1509 var leftKeys = this.keysArray(); | 1509 var leftKeys = this.keysArray(); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 1538 removed.push(this.get(leftKey)); | 1538 removed.push(this.get(leftKey)); |
| 1539 } | 1539 } |
| 1540 while (rightIndex < rightKeys.length) { | 1540 while (rightIndex < rightKeys.length) { |
| 1541 var rightKey = rightKeys[rightIndex++]; | 1541 var rightKey = rightKeys[rightIndex++]; |
| 1542 added.push(other.get(rightKey)); | 1542 added.push(other.get(rightKey)); |
| 1543 } | 1543 } |
| 1544 return { | 1544 return { |
| 1545 added: added, | 1545 added: added, |
| 1546 removed: removed, | 1546 removed: removed, |
| 1547 equal: equal | 1547 equal: equal |
| 1548 } | 1548 }; |
| 1549 } | 1549 }; |
| OLD | NEW |