Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/platform/utilities.js

Issue 2466123002: DevTools: reformat front-end code to match chromium style. (Closed)
Patch Set: all done Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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.
11 * 2. Redistributions in binary form must reproduce the above copyright 11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the 12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution. 13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived 15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission. 16 * from this software without specific prior written permission.
17 * 17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */ 28 */
29
30 // FIXME: This performance optimization should be moved to blink so that all dev elopers could enjoy it. 29 // 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. 30 // console is retrieved with V8Window.getAttribute method which is slow. Here we copy it to a js variable for faster access.
32 console = console; 31 console = console;
33 console.__originalAssert = console.assert; 32 console.__originalAssert = console.assert;
34 console.assert = function(value, message) 33 console.assert = function(value, message) {
35 { 34 if (value)
36 if (value) 35 return;
37 return; 36 console.__originalAssert(value, message);
38 console.__originalAssert(value, message);
39 }; 37 };
40 38
41 /** @typedef {Array|NodeList|Arguments|{length: number}} */ 39 /** @typedef {Array|NodeList|Arguments|{length: number}} */
42 var ArrayLike; 40 var ArrayLike;
43 41
44 /** 42 /**
45 * @param {number} m 43 * @param {number} m
46 * @param {number} n 44 * @param {number} n
47 * @return {number} 45 * @return {number}
48 */ 46 */
49 function mod(m, n) 47 function mod(m, n) {
50 { 48 return ((m % n) + n) % n;
51 return ((m % n) + n) % n;
52 } 49 }
53 50
54 /** 51 /**
55 * @param {string} string 52 * @param {string} string
56 * @return {!Array.<number>} 53 * @return {!Array.<number>}
57 */ 54 */
58 String.prototype.findAll = function(string) 55 String.prototype.findAll = function(string) {
59 { 56 var matches = [];
60 var matches = []; 57 var i = this.indexOf(string);
61 var i = this.indexOf(string); 58 while (i !== -1) {
62 while (i !== -1) { 59 matches.push(i);
63 matches.push(i); 60 i = this.indexOf(string, i + string.length);
64 i = this.indexOf(string, i + string.length); 61 }
62 return matches;
63 };
64
65 /**
66 * @return {string}
67 */
68 String.prototype.reverse = function() {
69 return this.split('').reverse().join('');
70 };
71
72 /**
73 * @return {string}
74 */
75 String.prototype.replaceControlCharacters = function() {
76 // Replace C0 and C1 control character sets with printable character.
77 // Do not replace '\t', \n' and '\r'.
78 return this.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u0080-\u009f]/g, '�');
79 };
80
81 /**
82 * @return {boolean}
83 */
84 String.prototype.isWhitespace = function() {
85 return /^\s*$/.test(this);
86 };
87
88 /**
89 * @return {!Array.<number>}
90 */
91 String.prototype.computeLineEndings = function() {
92 var endings = this.findAll('\n');
93 endings.push(this.length);
94 return endings;
95 };
96
97 /**
98 * @param {string} chars
99 * @return {string}
100 */
101 String.prototype.escapeCharacters = function(chars) {
102 var foundChar = false;
103 for (var i = 0; i < chars.length; ++i) {
104 if (this.indexOf(chars.charAt(i)) !== -1) {
105 foundChar = true;
106 break;
65 } 107 }
66 return matches; 108 }
67 }; 109
68 110 if (!foundChar)
69 /** 111 return String(this);
70 * @return {string} 112
71 */ 113 var result = '';
72 String.prototype.reverse = function() 114 for (var i = 0; i < this.length; ++i) {
73 { 115 if (chars.indexOf(this.charAt(i)) !== -1)
74 return this.split("").reverse().join(""); 116 result += '\\';
75 }; 117 result += this.charAt(i);
76 118 }
77 /** 119
78 * @return {string} 120 return result;
79 */ 121 };
80 String.prototype.replaceControlCharacters = function() 122
81 { 123 /**
82 // Replace C0 and C1 control character sets with printable character. 124 * @return {string}
83 // Do not replace '\t', \n' and '\r'. 125 */
84 return this.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u0080-\u009f]/g , "�"); 126 String.regexSpecialCharacters = function() {
85 }; 127 return '^[]{}()\\.^$*+?|-,';
86 128 };
87 /** 129
88 * @return {boolean} 130 /**
89 */ 131 * @return {string}
90 String.prototype.isWhitespace = function() 132 */
91 { 133 String.prototype.escapeForRegExp = function() {
92 return /^\s*$/.test(this); 134 return this.escapeCharacters(String.regexSpecialCharacters());
93 }; 135 };
94 136
95 /** 137 /**
96 * @return {!Array.<number>} 138 * @return {string}
97 */ 139 */
98 String.prototype.computeLineEndings = function() 140 String.prototype.escapeHTML = function() {
99 { 141 return this.replace(/&/g, '&amp;')
100 var endings = this.findAll("\n"); 142 .replace(/</g, '&lt;')
101 endings.push(this.length); 143 .replace(/>/g, '&gt;')
102 return endings; 144 .replace(/"/g, '&quot;'); // " doublequotes just for editor
103 }; 145 };
104 146
105 /** 147 /**
106 * @param {string} chars 148 * @return {string}
107 * @return {string} 149 */
108 */ 150 String.prototype.unescapeHTML = function() {
109 String.prototype.escapeCharacters = function(chars) 151 return this.replace(/&lt;/g, '<')
110 { 152 .replace(/&gt;/g, '>')
111 var foundChar = false; 153 .replace(/&#58;/g, ':')
112 for (var i = 0; i < chars.length; ++i) { 154 .replace(/&quot;/g, '"')
113 if (this.indexOf(chars.charAt(i)) !== -1) { 155 .replace(/&#60;/g, '<')
114 foundChar = true; 156 .replace(/&#62;/g, '>')
115 break; 157 .replace(/&amp;/g, '&');
116 } 158 };
117 } 159
118 160 /**
119 if (!foundChar) 161 * @return {string}
120 return String(this); 162 */
121 163 String.prototype.collapseWhitespace = function() {
122 var result = ""; 164 return this.replace(/[\s\xA0]+/g, ' ');
123 for (var i = 0; i < this.length; ++i) {
124 if (chars.indexOf(this.charAt(i)) !== -1)
125 result += "\\";
126 result += this.charAt(i);
127 }
128
129 return result;
130 };
131
132 /**
133 * @return {string}
134 */
135 String.regexSpecialCharacters = function()
136 {
137 return "^[]{}()\\.^$*+?|-,";
138 };
139
140 /**
141 * @return {string}
142 */
143 String.prototype.escapeForRegExp = function()
144 {
145 return this.escapeCharacters(String.regexSpecialCharacters());
146 };
147
148 /**
149 * @return {string}
150 */
151 String.prototype.escapeHTML = function()
152 {
153 return this.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt; ").replace(/"/g, "&quot;"); // " doublequotes just for editor
154 };
155
156 /**
157 * @return {string}
158 */
159 String.prototype.unescapeHTML = function()
160 {
161 return this.replace(/&lt;/g, "<")
162 .replace(/&gt;/g, ">")
163 .replace(/&#58;/g, ":")
164 .replace(/&quot;/g, "\"")
165 .replace(/&#60;/g, "<")
166 .replace(/&#62;/g, ">")
167 .replace(/&amp;/g, "&");
168 };
169
170 /**
171 * @return {string}
172 */
173 String.prototype.collapseWhitespace = function()
174 {
175 return this.replace(/[\s\xA0]+/g, " ");
176 }; 165 };
177 166
178 /** 167 /**
179 * @param {number} maxLength 168 * @param {number} maxLength
180 * @return {string} 169 * @return {string}
181 */ 170 */
182 String.prototype.trimMiddle = function(maxLength) 171 String.prototype.trimMiddle = function(maxLength) {
183 { 172 if (this.length <= maxLength)
184 if (this.length <= maxLength) 173 return String(this);
185 return String(this); 174 var leftHalf = maxLength >> 1;
186 var leftHalf = maxLength >> 1; 175 var rightHalf = maxLength - leftHalf - 1;
187 var rightHalf = maxLength - leftHalf - 1; 176 return this.substr(0, leftHalf) + '\u2026' + this.substr(this.length - rightHa lf, rightHalf);
188 return this.substr(0, leftHalf) + "\u2026" + this.substr(this.length - right Half, rightHalf);
189 }; 177 };
190 178
191 /** 179 /**
192 * @param {number} maxLength 180 * @param {number} maxLength
193 * @return {string} 181 * @return {string}
194 */ 182 */
195 String.prototype.trimEnd = function(maxLength) 183 String.prototype.trimEnd = function(maxLength) {
196 { 184 if (this.length <= maxLength)
197 if (this.length <= maxLength) 185 return String(this);
198 return String(this); 186 return this.substr(0, maxLength - 1) + '\u2026';
199 return this.substr(0, maxLength - 1) + "\u2026";
200 }; 187 };
201 188
202 /** 189 /**
203 * @param {?string=} baseURLDomain 190 * @param {?string=} baseURLDomain
204 * @return {string} 191 * @return {string}
205 */ 192 */
206 String.prototype.trimURL = function(baseURLDomain) 193 String.prototype.trimURL = function(baseURLDomain) {
207 { 194 var result = this.replace(/^(https|http|file):\/\//i, '');
208 var result = this.replace(/^(https|http|file):\/\//i, ""); 195 if (baseURLDomain) {
209 if (baseURLDomain) { 196 if (result.toLowerCase().startsWith(baseURLDomain.toLowerCase()))
210 if (result.toLowerCase().startsWith(baseURLDomain.toLowerCase())) 197 result = result.substr(baseURLDomain.length);
211 result = result.substr(baseURLDomain.length); 198 }
212 } 199 return result;
213 return result; 200 };
214 }; 201
215 202 /**
216 /** 203 * @return {string}
217 * @return {string} 204 */
218 */ 205 String.prototype.toTitleCase = function() {
219 String.prototype.toTitleCase = function() 206 return this.substring(0, 1).toUpperCase() + this.substring(1);
220 {
221 return this.substring(0, 1).toUpperCase() + this.substring(1);
222 }; 207 };
223 208
224 /** 209 /**
225 * @param {string} other 210 * @param {string} other
226 * @return {number} 211 * @return {number}
227 */ 212 */
228 String.prototype.compareTo = function(other) 213 String.prototype.compareTo = function(other) {
229 { 214 if (this > other)
230 if (this > other) 215 return 1;
231 return 1; 216 if (this < other)
232 if (this < other) 217 return -1;
233 return -1; 218 return 0;
234 return 0; 219 };
235 }; 220
236 221 /**
237 /** 222 * @return {string}
238 * @return {string} 223 */
239 */ 224 String.prototype.removeURLFragment = function() {
240 String.prototype.removeURLFragment = function() 225 var fragmentIndex = this.indexOf('#');
241 { 226 if (fragmentIndex === -1)
242 var fragmentIndex = this.indexOf("#"); 227 fragmentIndex = this.length;
243 if (fragmentIndex === -1) 228 return this.substring(0, fragmentIndex);
244 fragmentIndex = this.length;
245 return this.substring(0, fragmentIndex);
246 }; 229 };
247 230
248 /** 231 /**
249 * @param {string|undefined} string 232 * @param {string|undefined} string
250 * @return {number} 233 * @return {number}
251 */ 234 */
252 String.hashCode = function(string) 235 String.hashCode = function(string) {
253 { 236 if (!string)
254 if (!string) 237 return 0;
255 return 0; 238 // Hash algorithm for substrings is described in "Über die Komplexität der Mul tiplikation in
256 // Hash algorithm for substrings is described in "Über die Komplexität der M ultiplikation in 239 // eingeschränkten Branchingprogrammmodellen" by Woelfe.
257 // eingeschränkten Branchingprogrammmodellen" by Woelfe. 240 // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SE CTION00832000000000000000
258 // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html# SECTION00832000000000000000 241 var p = ((1 << 30) * 4 - 5); // prime: 2^32 - 5
259 var p = ((1 << 30) * 4 - 5); // prime: 2^32 - 5 242 var z = 0x5033d967; // 32 bits from random.org
260 var z = 0x5033d967; // 32 bits from random.org 243 var z2 = 0x59d2f15d; // random odd 32 bit number
261 var z2 = 0x59d2f15d; // random odd 32 bit number 244 var s = 0;
262 var s = 0; 245 var zi = 1;
263 var zi = 1; 246 for (var i = 0; i < string.length; i++) {
264 for (var i = 0; i < string.length; i++) { 247 var xi = string.charCodeAt(i) * z2;
265 var xi = string.charCodeAt(i) * z2; 248 s = (s + zi * xi) % p;
266 s = (s + zi * xi) % p; 249 zi = (zi * z) % p;
267 zi = (zi * z) % p; 250 }
268 } 251 s = (s + zi * (p - 1)) % p;
269 s = (s + zi * (p - 1)) % p; 252 return Math.abs(s | 0);
270 return Math.abs(s | 0); 253 };
271 }; 254
272 255 /**
273 /**
274 * @param {string} string 256 * @param {string} string
275 * @param {number} index 257 * @param {number} index
276 * @return {boolean} 258 * @return {boolean}
277 */ 259 */
278 String.isDigitAt = function(string, index) 260 String.isDigitAt = function(string, index) {
279 { 261 var c = string.charCodeAt(index);
280 var c = string.charCodeAt(index); 262 return (48 <= c && c <= 57);
281 return (48 <= c && c <= 57);
282 }; 263 };
283 264
284 /** 265 /**
285 * @return {string} 266 * @return {string}
286 */ 267 */
287 String.prototype.toBase64 = function() 268 String.prototype.toBase64 = function() {
288 { 269 /**
289 /** 270 * @param {number} b
290 * @param {number} b 271 * @return {number}
291 * @return {number} 272 */
292 */ 273 function encodeBits(b) {
293 function encodeBits(b) 274 return b < 26 ? b + 65 : b < 52 ? b + 71 : b < 62 ? b - 4 : b === 62 ? 43 : b === 63 ? 47 : 65;
294 { 275 }
295 return b < 26 ? b + 65 : b < 52 ? b + 71 : b < 62 ? b - 4 : b === 62 ? 4 3 : b === 63 ? 47 : 65; 276 var encoder = new TextEncoder();
277 var data = encoder.encode(this.toString());
278 var n = data.length;
279 var encoded = '';
280 if (n === 0)
281 return encoded;
282 var shift;
283 var v = 0;
284 for (var i = 0; i < n; i++) {
285 shift = i % 3;
286 v |= data[i] << (16 >>> shift & 24);
287 if (shift === 2) {
288 encoded += String.fromCharCode(
289 encodeBits(v >>> 18 & 63), encodeBits(v >>> 12 & 63), encodeBits(v >>> 6 & 63), encodeBits(v & 63));
290 v = 0;
296 } 291 }
297 var encoder = new TextEncoder(); 292 }
298 var data = encoder.encode(this.toString()); 293 if (shift === 0)
299 var n = data.length; 294 encoded += String.fromCharCode(encodeBits(v >>> 18 & 63), encodeBits(v >>> 1 2 & 63), 61, 61);
300 var encoded = ""; 295 else if (shift === 1)
301 if (n === 0) 296 encoded += String.fromCharCode(encodeBits(v >>> 18 & 63), encodeBits(v >>> 1 2 & 63), encodeBits(v >>> 6 & 63), 61);
302 return encoded; 297 return encoded;
303 var shift;
304 var v = 0;
305 for (var i = 0; i < n; i++) {
306 shift = i % 3;
307 v |= data[i] << (16 >>> shift & 24);
308 if (shift === 2) {
309 encoded += String.fromCharCode(encodeBits(v >>> 18 & 63), encodeBits (v >>> 12 & 63), encodeBits(v >>> 6 & 63), encodeBits(v & 63));
310 v = 0;
311 }
312 }
313 if (shift === 0)
314 encoded += String.fromCharCode(encodeBits(v >>> 18 & 63), encodeBits(v > >> 12 & 63), 61, 61);
315 else if (shift === 1)
316 encoded += String.fromCharCode(encodeBits(v >>> 18 & 63), encodeBits(v > >> 12 & 63), encodeBits(v >>> 6 & 63), 61);
317 return encoded;
318 }; 298 };
319 299
320 /** 300 /**
321 * @param {string} a 301 * @param {string} a
322 * @param {string} b 302 * @param {string} b
323 * @return {number} 303 * @return {number}
324 */ 304 */
325 String.naturalOrderComparator = function(a, b) 305 String.naturalOrderComparator = function(a, b) {
326 { 306 var chunk = /^\d+|^\D+/;
327 var chunk = /^\d+|^\D+/; 307 var chunka, chunkb, anum, bnum;
328 var chunka, chunkb, anum, bnum; 308 while (1) {
329 while (1) { 309 if (a) {
330 if (a) { 310 if (!b)
331 if (!b) 311 return 1;
332 return 1; 312 } else {
333 } else { 313 if (b)
334 if (b) 314 return -1;
335 return -1; 315 else
336 else 316 return 0;
337 return 0;
338 }
339 chunka = a.match(chunk)[0];
340 chunkb = b.match(chunk)[0];
341 anum = !isNaN(chunka);
342 bnum = !isNaN(chunkb);
343 if (anum && !bnum)
344 return -1;
345 if (bnum && !anum)
346 return 1;
347 if (anum && bnum) {
348 var diff = chunka - chunkb;
349 if (diff)
350 return diff;
351 if (chunka.length !== chunkb.length) {
352 if (!+chunka && !+chunkb) // chunks are strings of all 0s (speci al case)
353 return chunka.length - chunkb.length;
354 else
355 return chunkb.length - chunka.length;
356 }
357 } else if (chunka !== chunkb)
358 return (chunka < chunkb) ? -1 : 1;
359 a = a.substring(chunka.length);
360 b = b.substring(chunkb.length);
361 } 317 }
318 chunka = a.match(chunk)[0];
319 chunkb = b.match(chunk)[0];
320 anum = !isNaN(chunka);
321 bnum = !isNaN(chunkb);
322 if (anum && !bnum)
323 return -1;
324 if (bnum && !anum)
325 return 1;
326 if (anum && bnum) {
327 var diff = chunka - chunkb;
328 if (diff)
329 return diff;
330 if (chunka.length !== chunkb.length) {
331 if (! + chunka && ! + chunkb) // chunks are strings of all 0s (special case)
332 return chunka.length - chunkb.length;
333 else
334 return chunkb.length - chunka.length;
335 }
336 } else if (chunka !== chunkb)
337 return (chunka < chunkb) ? -1 : 1;
338 a = a.substring(chunka.length);
339 b = b.substring(chunkb.length);
340 }
362 }; 341 };
363 342
364 /** 343 /**
365 * @param {string} a 344 * @param {string} a
366 * @param {string} b 345 * @param {string} b
367 * @return {number} 346 * @return {number}
368 */ 347 */
369 String.caseInsensetiveComparator = function(a, b) 348 String.caseInsensetiveComparator = function(a, b) {
370 { 349 a = a.toUpperCase();
371 a = a.toUpperCase(); 350 b = b.toUpperCase();
372 b = b.toUpperCase(); 351 if (a === b)
373 if (a === b) 352 return 0;
374 return 0; 353 return a > b ? 1 : -1;
375 return a > b ? 1 : -1;
376 }; 354 };
377 355
378 /** 356 /**
379 * @param {number} num 357 * @param {number} num
380 * @param {number} min 358 * @param {number} min
381 * @param {number} max 359 * @param {number} max
382 * @return {number} 360 * @return {number}
383 */ 361 */
384 Number.constrain = function(num, min, max) 362 Number.constrain = function(num, min, max) {
385 { 363 if (num < min)
386 if (num < min) 364 num = min;
387 num = min; 365 else if (num > max)
388 else if (num > max) 366 num = max;
389 num = max; 367 return num;
390 return num;
391 }; 368 };
392 369
393 /** 370 /**
394 * @param {number} a 371 * @param {number} a
395 * @param {number} b 372 * @param {number} b
396 * @return {number} 373 * @return {number}
397 */ 374 */
398 Number.gcd = function(a, b) 375 Number.gcd = function(a, b) {
399 { 376 if (b === 0)
400 if (b === 0) 377 return a;
401 return a; 378 else
402 else 379 return Number.gcd(b, a % b);
403 return Number.gcd(b, a % b);
404 }; 380 };
405 381
406 /** 382 /**
407 * @param {string} value 383 * @param {string} value
408 * @return {string} 384 * @return {string}
409 */ 385 */
410 Number.toFixedIfFloating = function(value) 386 Number.toFixedIfFloating = function(value) {
411 { 387 if (!value || isNaN(value))
412 if (!value || isNaN(value)) 388 return value;
413 return value; 389 var number = Number(value);
414 var number = Number(value); 390 return number % 1 ? number.toFixed(3) : String(number);
415 return number % 1 ? number.toFixed(3) : String(number);
416 }; 391 };
417 392
418 /** 393 /**
419 * @return {boolean} 394 * @return {boolean}
420 */ 395 */
421 Date.prototype.isValid = function() 396 Date.prototype.isValid = function() {
422 { 397 return !isNaN(this.getTime());
423 return !isNaN(this.getTime());
424 }; 398 };
425 399
426 /** 400 /**
427 * @return {string} 401 * @return {string}
428 */ 402 */
429 Date.prototype.toISO8601Compact = function() 403 Date.prototype.toISO8601Compact = function() {
430 { 404 /**
431 /** 405 * @param {number} x
432 * @param {number} x 406 * @return {string}
433 * @return {string} 407 */
434 */ 408 function leadZero(x) {
435 function leadZero(x) 409 return (x > 9 ? '' : '0') + x;
436 { 410 }
437 return (x > 9 ? "" : "0") + x; 411 return this.getFullYear() + leadZero(this.getMonth() + 1) + leadZero(this.getD ate()) + 'T' +
438 } 412 leadZero(this.getHours()) + leadZero(this.getMinutes()) + leadZero(this.ge tSeconds());
439 return this.getFullYear() +
440 leadZero(this.getMonth() + 1) +
441 leadZero(this.getDate()) + "T" +
442 leadZero(this.getHours()) +
443 leadZero(this.getMinutes()) +
444 leadZero(this.getSeconds());
445 }; 413 };
446 414
447 /** 415 /**
448 * @return {string} 416 * @return {string}
449 */ 417 */
450 Date.prototype.toConsoleTime = function() 418 Date.prototype.toConsoleTime = function() {
451 { 419 /**
452 /** 420 * @param {number} x
453 * @param {number} x 421 * @return {string}
454 * @return {string} 422 */
455 */ 423 function leadZero2(x) {
456 function leadZero2(x) 424 return (x > 9 ? '' : '0') + x;
457 { 425 }
458 return (x > 9 ? "" : "0") + x; 426
459 } 427 /**
460 428 * @param {number} x
461 /** 429 * @return {string}
462 * @param {number} x 430 */
463 * @return {string} 431 function leadZero3(x) {
464 */ 432 return '0'.repeat(3 - x.toString().length) + x;
465 function leadZero3(x) 433 }
466 { 434
467 return "0".repeat(3 - x.toString().length) + x; 435 return this.getFullYear() + '-' + leadZero2(this.getMonth() + 1) + '-' + leadZ ero2(this.getDate()) + ' ' +
468 } 436 leadZero2(this.getHours()) + ':' + leadZero2(this.getMinutes()) + ':' + le adZero2(this.getSeconds()) + '.' +
469 437 leadZero3(this.getMilliseconds());
470 return this.getFullYear() + "-" +
471 leadZero2(this.getMonth() + 1) + "-" +
472 leadZero2(this.getDate()) + " " +
473 leadZero2(this.getHours()) + ":" +
474 leadZero2(this.getMinutes()) + ":" +
475 leadZero2(this.getSeconds()) + "." +
476 leadZero3(this.getMilliseconds());
477 }; 438 };
478 439
479 Object.defineProperty(Array.prototype, "remove", { 440 Object.defineProperty(Array.prototype, 'remove', {
480 /** 441 /**
481 * @param {!T} value 442 * @param {!T} value
482 * @param {boolean=} firstOnly 443 * @param {boolean=} firstOnly
483 * @return {boolean} 444 * @return {boolean}
484 * @this {Array.<!T>} 445 * @this {Array.<!T>}
446 * @template T
447 */
448 value: function(value, firstOnly) {
449 var index = this.indexOf(value);
450 if (index === -1)
451 return false;
452 if (firstOnly) {
453 this.splice(index, 1);
454 return true;
455 }
456 for (var i = index + 1, n = this.length; i < n; ++i) {
457 if (this[i] !== value)
458 this[index++] = this[i];
459 }
460 this.length = index;
461 return true;
462 }
463 });
464
465 Object.defineProperty(Array.prototype, 'pushAll', {
466 /**
467 * @param {!Array<!T>} array
468 * @this {Array<!T>}
469 * @template T
470 */
471 value: function(array) {
472 for (var i = 0; i < array.length; ++i)
473 this.push(array[i]);
474 }
475 });
476
477 Object.defineProperty(Array.prototype, 'rotate', {
478 /**
479 * @param {number} index
480 * @return {!Array.<!T>}
481 * @this {Array.<!T>}
482 * @template T
483 */
484 value: function(index) {
485 var result = [];
486 for (var i = index; i < index + this.length; ++i)
487 result.push(this[i % this.length]);
488 return result;
489 }
490 });
491
492 Object.defineProperty(Array.prototype, 'sortNumbers', {
493 /**
494 * @this {Array.<number>}
495 */
496 value: function() {
497 /**
498 * @param {number} a
499 * @param {number} b
500 * @return {number}
501 */
502 function numericComparator(a, b) {
503 return a - b;
504 }
505
506 this.sort(numericComparator);
507 }
508 });
509
510 Object.defineProperty(Uint32Array.prototype, 'sort', {value: Array.prototype.sor t});
511
512 (function() {
513 var partition = {
514 /**
515 * @this {Array.<number>}
516 * @param {function(number, number): number} comparator
517 * @param {number} left
518 * @param {number} right
519 * @param {number} pivotIndex
520 */
521 value: function(comparator, left, right, pivotIndex) {
522 function swap(array, i1, i2) {
523 var temp = array[i1];
524 array[i1] = array[i2];
525 array[i2] = temp;
526 }
527
528 var pivotValue = this[pivotIndex];
529 swap(this, right, pivotIndex);
530 var storeIndex = left;
531 for (var i = left; i < right; ++i) {
532 if (comparator(this[i], pivotValue) < 0) {
533 swap(this, storeIndex, i);
534 ++storeIndex;
535 }
536 }
537 swap(this, right, storeIndex);
538 return storeIndex;
539 }
540 };
541 Object.defineProperty(Array.prototype, 'partition', partition);
542 Object.defineProperty(Uint32Array.prototype, 'partition', partition);
543
544 var sortRange = {
545 /**
546 * @param {function(number, number): number} comparator
547 * @param {number} leftBound
548 * @param {number} rightBound
549 * @param {number} sortWindowLeft
550 * @param {number} sortWindowRight
551 * @return {!Array.<number>}
552 * @this {Array.<number>}
553 */
554 value: function(comparator, leftBound, rightBound, sortWindowLeft, sortWindo wRight) {
555 function quickSortRange(array, comparator, left, right, sortWindowLeft, so rtWindowRight) {
556 if (right <= left)
557 return;
558 var pivotIndex = Math.floor(Math.random() * (right - left)) + left;
559 var pivotNewIndex = array.partition(comparator, left, right, pivotIndex) ;
560 if (sortWindowLeft < pivotNewIndex)
561 quickSortRange(array, comparator, left, pivotNewIndex - 1, sortWindowL eft, sortWindowRight);
562 if (pivotNewIndex < sortWindowRight)
563 quickSortRange(array, comparator, pivotNewIndex + 1, right, sortWindow Left, sortWindowRight);
564 }
565 if (leftBound === 0 && rightBound === (this.length - 1) && sortWindowLeft === 0 && sortWindowRight >= rightBound)
566 this.sort(comparator);
567 else
568 quickSortRange(this, comparator, leftBound, rightBound, sortWindowLeft, sortWindowRight);
569 return this;
570 }
571 };
572 Object.defineProperty(Array.prototype, 'sortRange', sortRange);
573 Object.defineProperty(Uint32Array.prototype, 'sortRange', sortRange);
574 })();
575
576 Object.defineProperty(Array.prototype, 'stableSort', {
577 /**
578 * @param {function(?T, ?T): number=} comparator
579 * @return {!Array.<?T>}
580 * @this {Array.<?T>}
581 * @template T
582 */
583 value: function(comparator) {
584 function defaultComparator(a, b) {
585 return a < b ? -1 : (a > b ? 1 : 0);
586 }
587 comparator = comparator || defaultComparator;
588
589 var indices = new Array(this.length);
590 for (var i = 0; i < this.length; ++i)
591 indices[i] = i;
592 var self = this;
593 /**
594 * @param {number} a
595 * @param {number} b
596 * @return {number}
597 */
598 function indexComparator(a, b) {
599 var result = comparator(self[a], self[b]);
600 return result ? result : a - b;
601 }
602 indices.sort(indexComparator);
603
604 for (var i = 0; i < this.length; ++i) {
605 if (indices[i] < 0 || i === indices[i])
606 continue;
607 var cyclical = i;
608 var saved = this[i];
609 while (true) {
610 var next = indices[cyclical];
611 indices[cyclical] = -1;
612 if (next === i) {
613 this[cyclical] = saved;
614 break;
615 } else {
616 this[cyclical] = this[next];
617 cyclical = next;
618 }
619 }
620 }
621 return this;
622 }
623 });
624
625 Object.defineProperty(Array.prototype, 'qselect', {
626 /**
627 * @param {number} k
628 * @param {function(number, number): number=} comparator
629 * @return {number|undefined}
630 * @this {Array.<number>}
631 */
632 value: function(k, comparator) {
633 if (k < 0 || k >= this.length)
634 return;
635 if (!comparator)
636 comparator = function(a, b) {
637 return a - b;
638 };
639
640 var low = 0;
641 var high = this.length - 1;
642 for (;;) {
643 var pivotPosition = this.partition(comparator, low, high, Math.floor((high + low) / 2));
644 if (pivotPosition === k)
645 return this[k];
646 else if (pivotPosition > k)
647 high = pivotPosition - 1;
648 else
649 low = pivotPosition + 1;
650 }
651 }
652 });
653
654 Object.defineProperty(Array.prototype, 'lowerBound', {
655 /**
656 * Return index of the leftmost element that is equal or greater
657 * than the specimen object. If there's no such element (i.e. all
658 * elements are smaller than the specimen) returns right bound.
659 * The function works for sorted array.
660 * When specified, |left| (inclusive) and |right| (exclusive) indices
661 * define the search window.
662 *
663 * @param {!T} object
664 * @param {function(!T,!S):number=} comparator
665 * @param {number=} left
666 * @param {number=} right
667 * @return {number}
668 * @this {Array.<!S>}
669 * @template T,S
670 */
671 value: function(object, comparator, left, right) {
672 function defaultComparator(a, b) {
673 return a < b ? -1 : (a > b ? 1 : 0);
674 }
675 comparator = comparator || defaultComparator;
676 var l = left || 0;
677 var r = right !== undefined ? right : this.length;
678 while (l < r) {
679 var m = (l + r) >> 1;
680 if (comparator(object, this[m]) > 0)
681 l = m + 1;
682 else
683 r = m;
684 }
685 return r;
686 }
687 });
688
689 Object.defineProperty(Array.prototype, 'upperBound', {
690 /**
691 * Return index of the leftmost element that is greater
692 * than the specimen object. If there's no such element (i.e. all
693 * elements are smaller or equal to the specimen) returns right bound.
694 * The function works for sorted array.
695 * When specified, |left| (inclusive) and |right| (exclusive) indices
696 * define the search window.
697 *
698 * @param {!T} object
699 * @param {function(!T,!S):number=} comparator
700 * @param {number=} left
701 * @param {number=} right
702 * @return {number}
703 * @this {Array.<!S>}
704 * @template T,S
705 */
706 value: function(object, comparator, left, right) {
707 function defaultComparator(a, b) {
708 return a < b ? -1 : (a > b ? 1 : 0);
709 }
710 comparator = comparator || defaultComparator;
711 var l = left || 0;
712 var r = right !== undefined ? right : this.length;
713 while (l < r) {
714 var m = (l + r) >> 1;
715 if (comparator(object, this[m]) >= 0)
716 l = m + 1;
717 else
718 r = m;
719 }
720 return r;
721 }
722 });
723
724 Object.defineProperty(Uint32Array.prototype, 'lowerBound', {value: Array.prototy pe.lowerBound});
725
726 Object.defineProperty(Uint32Array.prototype, 'upperBound', {value: Array.prototy pe.upperBound});
727
728 Object.defineProperty(Float64Array.prototype, 'lowerBound', {value: Array.protot ype.lowerBound});
729
730 Object.defineProperty(Array.prototype, 'binaryIndexOf', {
731 /**
732 * @param {!T} value
733 * @param {function(!T,!S):number} comparator
734 * @return {number}
735 * @this {Array.<!S>}
736 * @template T,S
737 */
738 value: function(value, comparator) {
739 var index = this.lowerBound(value, comparator);
740 return index < this.length && comparator(value, this[index]) === 0 ? index : -1;
741 }
742 });
743
744 Object.defineProperty(Array.prototype, 'select', {
745 /**
746 * @param {string} field
747 * @return {!Array.<!T>}
748 * @this {Array.<!Object.<string,!T>>}
749 * @template T
750 */
751 value: function(field) {
752 var result = new Array(this.length);
753 for (var i = 0; i < this.length; ++i)
754 result[i] = this[i][field];
755 return result;
756 }
757 });
758
759 Object.defineProperty(Array.prototype, 'peekLast', {
760 /**
761 * @return {!T|undefined}
762 * @this {Array.<!T>}
763 * @template T
764 */
765 value: function() {
766 return this[this.length - 1];
767 }
768 });
769
770 (function() {
771 /**
772 * @param {!Array.<T>} array1
773 * @param {!Array.<T>} array2
774 * @param {function(T,T):number} comparator
775 * @param {boolean} mergeNotIntersect
776 * @return {!Array.<T>}
777 * @template T
778 */
779 function mergeOrIntersect(array1, array2, comparator, mergeNotIntersect) {
780 var result = [];
781 var i = 0;
782 var j = 0;
783 while (i < array1.length && j < array2.length) {
784 var compareValue = comparator(array1[i], array2[j]);
785 if (mergeNotIntersect || !compareValue)
786 result.push(compareValue <= 0 ? array1[i] : array2[j]);
787 if (compareValue <= 0)
788 i++;
789 if (compareValue >= 0)
790 j++;
791 }
792 if (mergeNotIntersect) {
793 while (i < array1.length)
794 result.push(array1[i++]);
795 while (j < array2.length)
796 result.push(array2[j++]);
797 }
798 return result;
799 }
800
801 Object.defineProperty(Array.prototype, 'intersectOrdered', {
802 /**
803 * @param {!Array.<T>} array
804 * @param {function(T,T):number} comparator
805 * @return {!Array.<T>}
806 * @this {!Array.<T>}
485 * @template T 807 * @template T
486 */ 808 */
487 value: function(value, firstOnly) 809 value: function(array, comparator) {
488 { 810 return mergeOrIntersect(this, array, comparator, false);
489 var index = this.indexOf(value); 811 }
490 if (index === -1) 812 });
491 return false; 813
492 if (firstOnly) { 814 Object.defineProperty(Array.prototype, 'mergeOrdered', {
493 this.splice(index, 1); 815 /**
494 return true; 816 * @param {!Array.<T>} array
495 } 817 * @param {function(T,T):number} comparator
496 for (var i = index + 1, n = this.length; i < n; ++i) { 818 * @return {!Array.<T>}
497 if (this[i] !== value) 819 * @this {!Array.<T>}
498 this[index++] = this[i];
499 }
500 this.length = index;
501 return true;
502 }
503 });
504
505 Object.defineProperty(Array.prototype, "pushAll", {
506 /**
507 * @param {!Array<!T>} array
508 * @this {Array<!T>}
509 * @template T 820 * @template T
510 */ 821 */
511 value: function(array) 822 value: function(array, comparator) {
512 { 823 return mergeOrIntersect(this, array, comparator, true);
513 for (var i = 0; i < array.length; ++i) 824 }
514 this.push(array[i]); 825 });
515 }
516 });
517
518 Object.defineProperty(Array.prototype, "rotate", {
519 /**
520 * @param {number} index
521 * @return {!Array.<!T>}
522 * @this {Array.<!T>}
523 * @template T
524 */
525 value: function(index)
526 {
527 var result = [];
528 for (var i = index; i < index + this.length; ++i)
529 result.push(this[i % this.length]);
530 return result;
531 }
532 });
533
534 Object.defineProperty(Array.prototype, "sortNumbers", {
535 /**
536 * @this {Array.<number>}
537 */
538 value: function()
539 {
540 /**
541 * @param {number} a
542 * @param {number} b
543 * @return {number}
544 */
545 function numericComparator(a, b)
546 {
547 return a - b;
548 }
549
550 this.sort(numericComparator);
551 }
552 });
553
554 Object.defineProperty(Uint32Array.prototype, "sort", {
555 value: Array.prototype.sort
556 });
557
558 (function() {
559 var partition = {
560 /**
561 * @this {Array.<number>}
562 * @param {function(number, number): number} comparator
563 * @param {number} left
564 * @param {number} right
565 * @param {number} pivotIndex
566 */
567 value: function(comparator, left, right, pivotIndex)
568 {
569 function swap(array, i1, i2)
570 {
571 var temp = array[i1];
572 array[i1] = array[i2];
573 array[i2] = temp;
574 }
575
576 var pivotValue = this[pivotIndex];
577 swap(this, right, pivotIndex);
578 var storeIndex = left;
579 for (var i = left; i < right; ++i) {
580 if (comparator(this[i], pivotValue) < 0) {
581 swap(this, storeIndex, i);
582 ++storeIndex;
583 }
584 }
585 swap(this, right, storeIndex);
586 return storeIndex;
587 }
588 };
589 Object.defineProperty(Array.prototype, "partition", partition);
590 Object.defineProperty(Uint32Array.prototype, "partition", partition);
591
592 var sortRange = {
593 /**
594 * @param {function(number, number): number} comparator
595 * @param {number} leftBound
596 * @param {number} rightBound
597 * @param {number} sortWindowLeft
598 * @param {number} sortWindowRight
599 * @return {!Array.<number>}
600 * @this {Array.<number>}
601 */
602 value: function(comparator, leftBound, rightBound, sortWindowLeft, sortW indowRight)
603 {
604 function quickSortRange(array, comparator, left, right, sortWindowLe ft, sortWindowRight)
605 {
606 if (right <= left)
607 return;
608 var pivotIndex = Math.floor(Math.random() * (right - left)) + le ft;
609 var pivotNewIndex = array.partition(comparator, left, right, piv otIndex);
610 if (sortWindowLeft < pivotNewIndex)
611 quickSortRange(array, comparator, left, pivotNewIndex - 1, s ortWindowLeft, sortWindowRight);
612 if (pivotNewIndex < sortWindowRight)
613 quickSortRange(array, comparator, pivotNewIndex + 1, right, sortWindowLeft, sortWindowRight);
614 }
615 if (leftBound === 0 && rightBound === (this.length - 1) && sortWindo wLeft === 0 && sortWindowRight >= rightBound)
616 this.sort(comparator);
617 else
618 quickSortRange(this, comparator, leftBound, rightBound, sortWind owLeft, sortWindowRight);
619 return this;
620 }
621 };
622 Object.defineProperty(Array.prototype, "sortRange", sortRange);
623 Object.defineProperty(Uint32Array.prototype, "sortRange", sortRange);
624 })(); 826 })();
625 827
626 Object.defineProperty(Array.prototype, "stableSort", {
627 /**
628 * @param {function(?T, ?T): number=} comparator
629 * @return {!Array.<?T>}
630 * @this {Array.<?T>}
631 * @template T
632 */
633 value: function(comparator)
634 {
635 function defaultComparator(a, b)
636 {
637 return a < b ? -1 : (a > b ? 1 : 0);
638 }
639 comparator = comparator || defaultComparator;
640
641 var indices = new Array(this.length);
642 for (var i = 0; i < this.length; ++i)
643 indices[i] = i;
644 var self = this;
645 /**
646 * @param {number} a
647 * @param {number} b
648 * @return {number}
649 */
650 function indexComparator(a, b)
651 {
652 var result = comparator(self[a], self[b]);
653 return result ? result : a - b;
654 }
655 indices.sort(indexComparator);
656
657 for (var i = 0; i < this.length; ++i) {
658 if (indices[i] < 0 || i === indices[i])
659 continue;
660 var cyclical = i;
661 var saved = this[i];
662 while (true) {
663 var next = indices[cyclical];
664 indices[cyclical] = -1;
665 if (next === i) {
666 this[cyclical] = saved;
667 break;
668 } else {
669 this[cyclical] = this[next];
670 cyclical = next;
671 }
672 }
673 }
674 return this;
675 }
676 });
677
678 Object.defineProperty(Array.prototype, "qselect", {
679 /**
680 * @param {number} k
681 * @param {function(number, number): number=} comparator
682 * @return {number|undefined}
683 * @this {Array.<number>}
684 */
685 value: function(k, comparator)
686 {
687 if (k < 0 || k >= this.length)
688 return;
689 if (!comparator)
690 comparator = function(a, b) { return a - b; };
691
692 var low = 0;
693 var high = this.length - 1;
694 for (;;) {
695 var pivotPosition = this.partition(comparator, low, high, Math.floor ((high + low) / 2));
696 if (pivotPosition === k)
697 return this[k];
698 else if (pivotPosition > k)
699 high = pivotPosition - 1;
700 else
701 low = pivotPosition + 1;
702 }
703 }
704 });
705
706 Object.defineProperty(Array.prototype, "lowerBound", {
707 /**
708 * Return index of the leftmost element that is equal or greater
709 * than the specimen object. If there's no such element (i.e. all
710 * elements are smaller than the specimen) returns right bound.
711 * The function works for sorted array.
712 * When specified, |left| (inclusive) and |right| (exclusive) indices
713 * define the search window.
714 *
715 * @param {!T} object
716 * @param {function(!T,!S):number=} comparator
717 * @param {number=} left
718 * @param {number=} right
719 * @return {number}
720 * @this {Array.<!S>}
721 * @template T,S
722 */
723 value: function(object, comparator, left, right)
724 {
725 function defaultComparator(a, b)
726 {
727 return a < b ? -1 : (a > b ? 1 : 0);
728 }
729 comparator = comparator || defaultComparator;
730 var l = left || 0;
731 var r = right !== undefined ? right : this.length;
732 while (l < r) {
733 var m = (l + r) >> 1;
734 if (comparator(object, this[m]) > 0)
735 l = m + 1;
736 else
737 r = m;
738 }
739 return r;
740 }
741 });
742
743 Object.defineProperty(Array.prototype, "upperBound", {
744 /**
745 * Return index of the leftmost element that is greater
746 * than the specimen object. If there's no such element (i.e. all
747 * elements are smaller or equal to the specimen) returns right bound.
748 * The function works for sorted array.
749 * When specified, |left| (inclusive) and |right| (exclusive) indices
750 * define the search window.
751 *
752 * @param {!T} object
753 * @param {function(!T,!S):number=} comparator
754 * @param {number=} left
755 * @param {number=} right
756 * @return {number}
757 * @this {Array.<!S>}
758 * @template T,S
759 */
760 value: function(object, comparator, left, right)
761 {
762 function defaultComparator(a, b)
763 {
764 return a < b ? -1 : (a > b ? 1 : 0);
765 }
766 comparator = comparator || defaultComparator;
767 var l = left || 0;
768 var r = right !== undefined ? right : this.length;
769 while (l < r) {
770 var m = (l + r) >> 1;
771 if (comparator(object, this[m]) >= 0)
772 l = m + 1;
773 else
774 r = m;
775 }
776 return r;
777 }
778 });
779
780 Object.defineProperty(Uint32Array.prototype, "lowerBound", {
781 value: Array.prototype.lowerBound
782 });
783
784 Object.defineProperty(Uint32Array.prototype, "upperBound", {
785 value: Array.prototype.upperBound
786 });
787
788 Object.defineProperty(Float64Array.prototype, "lowerBound", {
789 value: Array.prototype.lowerBound
790 });
791
792 Object.defineProperty(Array.prototype, "binaryIndexOf", {
793 /**
794 * @param {!T} value
795 * @param {function(!T,!S):number} comparator
796 * @return {number}
797 * @this {Array.<!S>}
798 * @template T,S
799 */
800 value: function(value, comparator)
801 {
802 var index = this.lowerBound(value, comparator);
803 return index < this.length && comparator(value, this[index]) === 0 ? ind ex : -1;
804 }
805 });
806
807 Object.defineProperty(Array.prototype, "select", {
808 /**
809 * @param {string} field
810 * @return {!Array.<!T>}
811 * @this {Array.<!Object.<string,!T>>}
812 * @template T
813 */
814 value: function(field)
815 {
816 var result = new Array(this.length);
817 for (var i = 0; i < this.length; ++i)
818 result[i] = this[i][field];
819 return result;
820 }
821 });
822
823 Object.defineProperty(Array.prototype, "peekLast", {
824 /**
825 * @return {!T|undefined}
826 * @this {Array.<!T>}
827 * @template T
828 */
829 value: function()
830 {
831 return this[this.length - 1];
832 }
833 });
834
835 (function(){
836 /**
837 * @param {!Array.<T>} array1
838 * @param {!Array.<T>} array2
839 * @param {function(T,T):number} comparator
840 * @param {boolean} mergeNotIntersect
841 * @return {!Array.<T>}
842 * @template T
843 */
844 function mergeOrIntersect(array1, array2, comparator, mergeNotIntersect)
845 {
846 var result = [];
847 var i = 0;
848 var j = 0;
849 while (i < array1.length && j < array2.length) {
850 var compareValue = comparator(array1[i], array2[j]);
851 if (mergeNotIntersect || !compareValue)
852 result.push(compareValue <= 0 ? array1[i] : array2[j]);
853 if (compareValue <= 0)
854 i++;
855 if (compareValue >= 0)
856 j++;
857 }
858 if (mergeNotIntersect) {
859 while (i < array1.length)
860 result.push(array1[i++]);
861 while (j < array2.length)
862 result.push(array2[j++]);
863 }
864 return result;
865 }
866
867 Object.defineProperty(Array.prototype, "intersectOrdered", {
868 /**
869 * @param {!Array.<T>} array
870 * @param {function(T,T):number} comparator
871 * @return {!Array.<T>}
872 * @this {!Array.<T>}
873 * @template T
874 */
875 value: function(array, comparator)
876 {
877 return mergeOrIntersect(this, array, comparator, false);
878 }
879 });
880
881 Object.defineProperty(Array.prototype, "mergeOrdered", {
882 /**
883 * @param {!Array.<T>} array
884 * @param {function(T,T):number} comparator
885 * @return {!Array.<T>}
886 * @this {!Array.<T>}
887 * @template T
888 */
889 value: function(array, comparator)
890 {
891 return mergeOrIntersect(this, array, comparator, true);
892 }
893 });
894 })();
895
896 /** 828 /**
897 * @param {string} format 829 * @param {string} format
898 * @param {...*} var_arg 830 * @param {...*} var_arg
899 * @return {string} 831 * @return {string}
900 */ 832 */
901 String.sprintf = function(format, var_arg) 833 String.sprintf = function(format, var_arg) {
902 { 834 return String.vsprintf(format, Array.prototype.slice.call(arguments, 1));
903 return String.vsprintf(format, Array.prototype.slice.call(arguments, 1));
904 }; 835 };
905 836
906 /** 837 /**
907 * @param {string} format 838 * @param {string} format
908 * @param {!Object.<string, function(string, ...):*>} formatters 839 * @param {!Object.<string, function(string, ...):*>} formatters
909 * @return {!Array.<!Object>} 840 * @return {!Array.<!Object>}
910 */ 841 */
911 String.tokenizeFormatString = function(format, formatters) 842 String.tokenizeFormatString = function(format, formatters) {
912 { 843 var tokens = [];
913 var tokens = []; 844 var substitutionIndex = 0;
914 var substitutionIndex = 0;
915 845
916 function addStringToken(str) 846 function addStringToken(str) {
917 { 847 if (tokens.length && tokens[tokens.length - 1].type === 'string')
918 if (tokens.length && tokens[tokens.length - 1].type === "string") 848 tokens[tokens.length - 1].value += str;
919 tokens[tokens.length - 1].value += str; 849 else
920 else 850 tokens.push({type: 'string', value: str});
921 tokens.push({ type: "string", value: str }); 851 }
852
853 function addSpecifierToken(specifier, precision, substitutionIndex) {
854 tokens.push({type: 'specifier', specifier: specifier, precision: precision, substitutionIndex: substitutionIndex});
855 }
856
857 var index = 0;
858 for (var precentIndex = format.indexOf('%', index); precentIndex !== -1; prece ntIndex = format.indexOf('%', index)) {
859 if (format.length === index) // unescaped % sign at the end of the format s tring.
860 break;
861 addStringToken(format.substring(index, precentIndex));
862 index = precentIndex + 1;
863
864 if (format[index] === '%') {
865 // %% escape sequence.
866 addStringToken('%');
867 ++index;
868 continue;
922 } 869 }
923 870
924 function addSpecifierToken(specifier, precision, substitutionIndex) 871 if (String.isDigitAt(format, index)) {
925 { 872 // The first character is a number, it might be a substitution index.
926 tokens.push({ type: "specifier", specifier: specifier, precision: precis ion, substitutionIndex: substitutionIndex }); 873 var number = parseInt(format.substring(index), 10);
874 while (String.isDigitAt(format, index))
875 ++index;
876
877 // If the number is greater than zero and ends with a "$",
878 // then this is a substitution index.
879 if (number > 0 && format[index] === '$') {
880 substitutionIndex = (number - 1);
881 ++index;
882 }
927 } 883 }
928 884
929 var index = 0; 885 var precision = -1;
930 for (var precentIndex = format.indexOf("%", index); precentIndex !== -1; pre centIndex = format.indexOf("%", index)) { 886 if (format[index] === '.') {
931 if (format.length === index) // unescaped % sign at the end of the form at string. 887 // This is a precision specifier. If no digit follows the ".",
932 break; 888 // then the precision should be zero.
933 addStringToken(format.substring(index, precentIndex)); 889 ++index;
934 index = precentIndex + 1; 890 precision = parseInt(format.substring(index), 10);
891 if (isNaN(precision))
892 precision = 0;
935 893
936 if (format[index] === "%") { 894 while (String.isDigitAt(format, index))
937 // %% escape sequence.
938 addStringToken("%");
939 ++index;
940 continue;
941 }
942
943 if (String.isDigitAt(format, index)) {
944 // The first character is a number, it might be a substitution index .
945 var number = parseInt(format.substring(index), 10);
946 while (String.isDigitAt(format, index))
947 ++index;
948
949 // If the number is greater than zero and ends with a "$",
950 // then this is a substitution index.
951 if (number > 0 && format[index] === "$") {
952 substitutionIndex = (number - 1);
953 ++index;
954 }
955 }
956
957 var precision = -1;
958 if (format[index] === ".") {
959 // This is a precision specifier. If no digit follows the ".",
960 // then the precision should be zero.
961 ++index;
962 precision = parseInt(format.substring(index), 10);
963 if (isNaN(precision))
964 precision = 0;
965
966 while (String.isDigitAt(format, index))
967 ++index;
968 }
969
970 if (!(format[index] in formatters)) {
971 addStringToken(format.substring(precentIndex, index + 1));
972 ++index;
973 continue;
974 }
975
976 addSpecifierToken(format[index], precision, substitutionIndex);
977
978 ++substitutionIndex;
979 ++index; 895 ++index;
980 } 896 }
981 897
982 addStringToken(format.substring(index)); 898 if (!(format[index] in formatters)) {
899 addStringToken(format.substring(precentIndex, index + 1));
900 ++index;
901 continue;
902 }
983 903
984 return tokens; 904 addSpecifierToken(format[index], precision, substitutionIndex);
905
906 ++substitutionIndex;
907 ++index;
908 }
909
910 addStringToken(format.substring(index));
911
912 return tokens;
985 }; 913 };
986 914
987 String.standardFormatters = { 915 String.standardFormatters = {
988 /** 916 /**
989 * @return {number} 917 * @return {number}
990 */ 918 */
991 d: function(substitution) 919 d: function(substitution) {
992 { 920 return !isNaN(substitution) ? substitution : 0;
993 return !isNaN(substitution) ? substitution : 0; 921 },
994 },
995 922
996 /** 923 /**
997 * @return {number} 924 * @return {number}
998 */ 925 */
999 f: function(substitution, token) 926 f: function(substitution, token) {
1000 { 927 if (substitution && token.precision > -1)
1001 if (substitution && token.precision > -1) 928 substitution = substitution.toFixed(token.precision);
1002 substitution = substitution.toFixed(token.precision); 929 return !isNaN(substitution) ? substitution : (token.precision > -1 ? Number( 0).toFixed(token.precision) : 0);
1003 return !isNaN(substitution) ? substitution : (token.precision > -1 ? Num ber(0).toFixed(token.precision) : 0); 930 },
1004 },
1005 931
1006 /** 932 /**
1007 * @return {string} 933 * @return {string}
1008 */ 934 */
1009 s: function(substitution) 935 s: function(substitution) {
1010 { 936 return substitution;
1011 return substitution; 937 }
1012 }
1013 }; 938 };
1014 939
1015 /** 940 /**
1016 * @param {string} format 941 * @param {string} format
1017 * @param {!Array.<*>} substitutions 942 * @param {!Array.<*>} substitutions
1018 * @return {string} 943 * @return {string}
1019 */ 944 */
1020 String.vsprintf = function(format, substitutions) 945 String.vsprintf = function(format, substitutions) {
1021 { 946 return String
1022 return String.format(format, substitutions, String.standardFormatters, "", f unction(a, b) { return a + b; }).formattedResult; 947 .format(
948 format, substitutions, String.standardFormatters, '',
949 function(a, b) {
950 return a + b;
951 })
952 .formattedResult;
1023 }; 953 };
1024 954
1025 /** 955 /**
1026 * @param {string} format 956 * @param {string} format
1027 * @param {?ArrayLike} substitutions 957 * @param {?ArrayLike} substitutions
1028 * @param {!Object.<string, function(string, ...):Q>} formatters 958 * @param {!Object.<string, function(string, ...):Q>} formatters
1029 * @param {!T} initialValue 959 * @param {!T} initialValue
1030 * @param {function(T, Q): T|undefined} append 960 * @param {function(T, Q): T|undefined} append
1031 * @param {!Array.<!Object>=} tokenizedFormat 961 * @param {!Array.<!Object>=} tokenizedFormat
1032 * @return {!{formattedResult: T, unusedSubstitutions: ?ArrayLike}}; 962 * @return {!{formattedResult: T, unusedSubstitutions: ?ArrayLike}};
1033 * @template T, Q 963 * @template T, Q
1034 */ 964 */
1035 String.format = function(format, substitutions, formatters, initialValue, append , tokenizedFormat) 965 String.format = function(format, substitutions, formatters, initialValue, append , tokenizedFormat) {
1036 { 966 if (!format || !substitutions || !substitutions.length)
1037 if (!format || !substitutions || !substitutions.length) 967 return {formattedResult: append(initialValue, format), unusedSubstitutions: substitutions};
1038 return { formattedResult: append(initialValue, format), unusedSubstituti ons: substitutions };
1039 968
1040 function prettyFunctionName() 969 function prettyFunctionName() {
1041 { 970 return 'String.format("' + format + '", "' + Array.prototype.join.call(subst itutions, '", "') + '")';
1042 return "String.format(\"" + format + "\", \"" + Array.prototype.join.cal l(substitutions, "\", \"") + "\")"; 971 }
972
973 function warn(msg) {
974 console.warn(prettyFunctionName() + ': ' + msg);
975 }
976
977 function error(msg) {
978 console.error(prettyFunctionName() + ': ' + msg);
979 }
980
981 var result = initialValue;
982 var tokens = tokenizedFormat || String.tokenizeFormatString(format, formatters );
983 var usedSubstitutionIndexes = {};
984
985 for (var i = 0; i < tokens.length; ++i) {
986 var token = tokens[i];
987
988 if (token.type === 'string') {
989 result = append(result, token.value);
990 continue;
1043 } 991 }
1044 992
1045 function warn(msg) 993 if (token.type !== 'specifier') {
1046 { 994 error('Unknown token type "' + token.type + '" found.');
1047 console.warn(prettyFunctionName() + ": " + msg); 995 continue;
1048 } 996 }
1049 997
1050 function error(msg) 998 if (token.substitutionIndex >= substitutions.length) {
1051 { 999 // If there are not enough substitutions for the current substitutionIndex
1052 console.error(prettyFunctionName() + ": " + msg); 1000 // just output the format specifier literally and move on.
1001 error(
1002 'not enough substitution arguments. Had ' + substitutions.length + ' b ut needed ' +
1003 (token.substitutionIndex + 1) + ', so substitution was skipped.');
1004 result = append(result, '%' + (token.precision > -1 ? token.precision : '' ) + token.specifier);
1005 continue;
1053 } 1006 }
1054 1007
1055 var result = initialValue; 1008 usedSubstitutionIndexes[token.substitutionIndex] = true;
1056 var tokens = tokenizedFormat || String.tokenizeFormatString(format, formatte rs);
1057 var usedSubstitutionIndexes = {};
1058 1009
1059 for (var i = 0; i < tokens.length; ++i) { 1010 if (!(token.specifier in formatters)) {
1060 var token = tokens[i]; 1011 // Encountered an unsupported format character, treat as a string.
1061 1012 warn('unsupported format character \u201C' + token.specifier + '\u201D. Tr eating as a string.');
1062 if (token.type === "string") { 1013 result = append(result, substitutions[token.substitutionIndex]);
1063 result = append(result, token.value); 1014 continue;
1064 continue;
1065 }
1066
1067 if (token.type !== "specifier") {
1068 error("Unknown token type \"" + token.type + "\" found.");
1069 continue;
1070 }
1071
1072 if (token.substitutionIndex >= substitutions.length) {
1073 // If there are not enough substitutions for the current substitutio nIndex
1074 // just output the format specifier literally and move on.
1075 error("not enough substitution arguments. Had " + substitutions.leng th + " but needed " + (token.substitutionIndex + 1) + ", so substitution was ski pped.");
1076 result = append(result, "%" + (token.precision > -1 ? token.precisio n : "") + token.specifier);
1077 continue;
1078 }
1079
1080 usedSubstitutionIndexes[token.substitutionIndex] = true;
1081
1082 if (!(token.specifier in formatters)) {
1083 // Encountered an unsupported format character, treat as a string.
1084 warn("unsupported format character \u201C" + token.specifier + "\u20 1D. Treating as a string.");
1085 result = append(result, substitutions[token.substitutionIndex]);
1086 continue;
1087 }
1088
1089 result = append(result, formatters[token.specifier](substitutions[token. substitutionIndex], token));
1090 } 1015 }
1091 1016
1092 var unusedSubstitutions = []; 1017 result = append(result, formatters[token.specifier](substitutions[token.subs titutionIndex], token));
1093 for (var i = 0; i < substitutions.length; ++i) { 1018 }
1094 if (i in usedSubstitutionIndexes)
1095 continue;
1096 unusedSubstitutions.push(substitutions[i]);
1097 }
1098 1019
1099 return { formattedResult: result, unusedSubstitutions: unusedSubstitutions } ; 1020 var unusedSubstitutions = [];
1021 for (var i = 0; i < substitutions.length; ++i) {
1022 if (i in usedSubstitutionIndexes)
1023 continue;
1024 unusedSubstitutions.push(substitutions[i]);
1025 }
1026
1027 return {formattedResult: result, unusedSubstitutions: unusedSubstitutions};
1100 }; 1028 };
1101 1029
1102 /** 1030 /**
1103 * @param {string} query 1031 * @param {string} query
1104 * @param {boolean} caseSensitive 1032 * @param {boolean} caseSensitive
1105 * @param {boolean} isRegex 1033 * @param {boolean} isRegex
1106 * @return {!RegExp} 1034 * @return {!RegExp}
1107 */ 1035 */
1108 function createSearchRegex(query, caseSensitive, isRegex) 1036 function createSearchRegex(query, caseSensitive, isRegex) {
1109 { 1037 var regexFlags = caseSensitive ? 'g' : 'gi';
1110 var regexFlags = caseSensitive ? "g" : "gi"; 1038 var regexObject;
1111 var regexObject;
1112 1039
1113 if (isRegex) { 1040 if (isRegex) {
1114 try { 1041 try {
1115 regexObject = new RegExp(query, regexFlags); 1042 regexObject = new RegExp(query, regexFlags);
1116 } catch (e) { 1043 } catch (e) {
1117 // Silent catch. 1044 // Silent catch.
1118 }
1119 } 1045 }
1046 }
1120 1047
1121 if (!regexObject) 1048 if (!regexObject)
1122 regexObject = createPlainTextSearchRegex(query, regexFlags); 1049 regexObject = createPlainTextSearchRegex(query, regexFlags);
1123 1050
1124 return regexObject; 1051 return regexObject;
1125 } 1052 }
1126 1053
1127 /** 1054 /**
1128 * @param {string} query 1055 * @param {string} query
1129 * @param {string=} flags 1056 * @param {string=} flags
1130 * @return {!RegExp} 1057 * @return {!RegExp}
1131 */ 1058 */
1132 function createPlainTextSearchRegex(query, flags) 1059 function createPlainTextSearchRegex(query, flags) {
1133 { 1060 // This should be kept the same as the one in StringUtil.cpp.
1134 // This should be kept the same as the one in StringUtil.cpp. 1061 var regexSpecialCharacters = String.regexSpecialCharacters();
1135 var regexSpecialCharacters = String.regexSpecialCharacters(); 1062 var regex = '';
1136 var regex = ""; 1063 for (var i = 0; i < query.length; ++i) {
1137 for (var i = 0; i < query.length; ++i) { 1064 var c = query.charAt(i);
1138 var c = query.charAt(i); 1065 if (regexSpecialCharacters.indexOf(c) !== -1)
1139 if (regexSpecialCharacters.indexOf(c) !== -1) 1066 regex += '\\';
1140 regex += "\\"; 1067 regex += c;
1141 regex += c; 1068 }
1142 } 1069 return new RegExp(regex, flags || '');
1143 return new RegExp(regex, flags || "");
1144 } 1070 }
1145 1071
1146 /** 1072 /**
1147 * @param {!RegExp} regex 1073 * @param {!RegExp} regex
1148 * @param {string} content 1074 * @param {string} content
1149 * @return {number} 1075 * @return {number}
1150 */ 1076 */
1151 function countRegexMatches(regex, content) 1077 function countRegexMatches(regex, content) {
1152 { 1078 var text = content;
1153 var text = content; 1079 var result = 0;
1154 var result = 0; 1080 var match;
1155 var match; 1081 while (text && (match = regex.exec(text))) {
1156 while (text && (match = regex.exec(text))) { 1082 if (match[0].length > 0)
1157 if (match[0].length > 0) 1083 ++result;
1158 ++result; 1084 text = text.substring(match.index + 1);
1159 text = text.substring(match.index + 1); 1085 }
1160 } 1086 return result;
1161 return result;
1162 } 1087 }
1163 1088
1164 /** 1089 /**
1165 * @param {number} spacesCount 1090 * @param {number} spacesCount
1166 * @return {string} 1091 * @return {string}
1167 */ 1092 */
1168 function spacesPadding(spacesCount) 1093 function spacesPadding(spacesCount) {
1169 { 1094 return '\u00a0'.repeat(spacesCount);
1170 return "\u00a0".repeat(spacesCount);
1171 } 1095 }
1172 1096
1173 /** 1097 /**
1174 * @param {number} value 1098 * @param {number} value
1175 * @param {number} symbolsCount 1099 * @param {number} symbolsCount
1176 * @return {string} 1100 * @return {string}
1177 */ 1101 */
1178 function numberToStringWithSpacesPadding(value, symbolsCount) 1102 function numberToStringWithSpacesPadding(value, symbolsCount) {
1179 { 1103 var numberString = value.toString();
1180 var numberString = value.toString(); 1104 var paddingLength = Math.max(0, symbolsCount - numberString.length);
1181 var paddingLength = Math.max(0, symbolsCount - numberString.length); 1105 return spacesPadding(paddingLength) + numberString;
1182 return spacesPadding(paddingLength) + numberString;
1183 } 1106 }
1184 1107
1185 /** 1108 /**
1186 * @return {!Array.<T>} 1109 * @return {!Array.<T>}
1187 * @template T 1110 * @template T
1188 */ 1111 */
1189 Set.prototype.valuesArray = function() 1112 Set.prototype.valuesArray = function() {
1190 { 1113 return Array.from(this.values());
1191 return Array.from(this.values());
1192 }; 1114 };
1193 1115
1194 /** 1116 /**
1195 * @param {!Iterable<T>|!Array<!T>} iterable 1117 * @param {!Iterable<T>|!Array<!T>} iterable
1196 * @template T 1118 * @template T
1197 */ 1119 */
1198 Set.prototype.addAll = function(iterable) 1120 Set.prototype.addAll = function(iterable) {
1199 { 1121 for (var e of iterable)
1200 for (var e of iterable) 1122 this.add(e);
1201 this.add(e);
1202 }; 1123 };
1203 1124
1204 /** 1125 /**
1205 * @param {!Iterable<T>|!Array<!T>} iterable 1126 * @param {!Iterable<T>|!Array<!T>} iterable
1206 * @return {boolean} 1127 * @return {boolean}
1207 * @template T 1128 * @template T
1208 */ 1129 */
1209 Set.prototype.containsAll = function(iterable) 1130 Set.prototype.containsAll = function(iterable) {
1210 { 1131 for (var e of iterable) {
1211 for (var e of iterable) { 1132 if (!this.has(e))
1212 if (!this.has(e)) 1133 return false;
1213 return false; 1134 }
1214 } 1135 return true;
1215 return true;
1216 }; 1136 };
1217 1137
1218 /** 1138 /**
1219 * @return {T} 1139 * @return {T}
1220 * @template T 1140 * @template T
1221 */ 1141 */
1222 Map.prototype.remove = function(key) 1142 Map.prototype.remove = function(key) {
1223 { 1143 var value = this.get(key);
1144 this.delete(key);
1145 return value;
1146 };
1147
1148 /**
1149 * @return {!Array<!VALUE>}
1150 */
1151 Map.prototype.valuesArray = function() {
1152 return Array.from(this.values());
1153 };
1154
1155 /**
1156 * @return {!Array<!KEY>}
1157 */
1158 Map.prototype.keysArray = function() {
1159 return Array.from(this.keys());
1160 };
1161
1162 /**
1163 * @return {!Multimap<!KEY, !VALUE>}
1164 */
1165 Map.prototype.inverse = function() {
1166 var result = new Multimap();
1167 for (var key of this.keys()) {
1224 var value = this.get(key); 1168 var value = this.get(key);
1225 this.delete(key); 1169 result.set(value, key);
1226 return value; 1170 }
1227 }; 1171 return result;
1228
1229 /**
1230 * @return {!Array<!VALUE>}
1231 */
1232 Map.prototype.valuesArray = function()
1233 {
1234 return Array.from(this.values());
1235 };
1236
1237 /**
1238 * @return {!Array<!KEY>}
1239 */
1240 Map.prototype.keysArray = function()
1241 {
1242 return Array.from(this.keys());
1243 };
1244
1245 /**
1246 * @return {!Multimap<!KEY, !VALUE>}
1247 */
1248 Map.prototype.inverse = function()
1249 {
1250 var result = new Multimap();
1251 for (var key of this.keys()) {
1252 var value = this.get(key);
1253 result.set(value, key);
1254 }
1255 return result;
1256 }; 1172 };
1257 1173
1258 /** 1174 /**
1259 * @constructor 1175 * @constructor
1260 * @template K, V 1176 * @template K, V
1261 */ 1177 */
1262 var Multimap = function() 1178 var Multimap = function() {
1263 { 1179 /** @type {!Map.<K, !Set.<!V>>} */
1264 /** @type {!Map.<K, !Set.<!V>>} */ 1180 this._map = new Map();
1265 this._map = new Map();
1266 }; 1181 };
1267 1182
1268 Multimap.prototype = { 1183 Multimap.prototype = {
1269 /** 1184 /**
1270 * @param {K} key 1185 * @param {K} key
1271 * @param {V} value 1186 * @param {V} value
1272 */ 1187 */
1273 set: function(key, value) 1188 set: function(key, value) {
1274 { 1189 var set = this._map.get(key);
1275 var set = this._map.get(key); 1190 if (!set) {
1276 if (!set) { 1191 set = new Set();
1277 set = new Set(); 1192 this._map.set(key, set);
1278 this._map.set(key, set); 1193 }
1279 } 1194 set.add(value);
1280 set.add(value); 1195 },
1281 }, 1196
1282 1197 /**
1283 /** 1198 * @param {K} key
1284 * @param {K} key 1199 * @return {!Set.<!V>}
1285 * @return {!Set.<!V>} 1200 */
1286 */ 1201 get: function(key) {
1287 get: function(key) 1202 var result = this._map.get(key);
1288 { 1203 if (!result)
1289 var result = this._map.get(key); 1204 result = new Set();
1290 if (!result) 1205 return result;
1291 result = new Set(); 1206 },
1292 return result; 1207
1293 }, 1208 /**
1294 1209 * @param {K} key
1295 /** 1210 * @return {boolean}
1296 * @param {K} key 1211 */
1297 * @return {boolean} 1212 has: function(key) {
1298 */ 1213 return this._map.has(key);
1299 has: function(key) 1214 },
1300 { 1215
1301 return this._map.has(key); 1216 /**
1302 }, 1217 * @param {K} key
1303 1218 * @param {V} value
1304 /** 1219 * @return {boolean}
1305 * @param {K} key 1220 */
1306 * @param {V} value 1221 hasValue: function(key, value) {
1307 * @return {boolean} 1222 var set = this._map.get(key);
1308 */ 1223 if (!set)
1309 hasValue: function(key, value) 1224 return false;
1310 { 1225 return set.has(value);
1311 var set = this._map.get(key); 1226 },
1312 if (!set) 1227
1313 return false; 1228 /**
1314 return set.has(value); 1229 * @return {number}
1315 }, 1230 */
1316 1231 get size() {
1317 /** 1232 return this._map.size;
1318 * @return {number} 1233 },
1319 */ 1234
1320 get size() 1235 /**
1321 { 1236 * @param {K} key
1322 return this._map.size; 1237 * @param {V} value
1323 }, 1238 */
1324 1239 remove: function(key, value) {
1325 /** 1240 var values = this.get(key);
1326 * @param {K} key 1241 values.delete(value);
1327 * @param {V} value 1242 if (!values.size)
1328 */ 1243 this._map.delete(key);
1329 remove: function(key, value) 1244 },
1330 { 1245
1331 var values = this.get(key); 1246 /**
1332 values.delete(value); 1247 * @param {K} key
1333 if (!values.size) 1248 */
1334 this._map.delete(key); 1249 removeAll: function(key) {
1335 }, 1250 this._map.delete(key);
1336 1251 },
1337 /** 1252
1338 * @param {K} key 1253 /**
1339 */ 1254 * @return {!Array.<K>}
1340 removeAll: function(key) 1255 */
1341 { 1256 keysArray: function() {
1342 this._map.delete(key); 1257 return this._map.keysArray();
1343 }, 1258 },
1344 1259
1345 /** 1260 /**
1346 * @return {!Array.<K>} 1261 * @return {!Array.<!V>}
1347 */ 1262 */
1348 keysArray: function() 1263 valuesArray: function() {
1349 { 1264 var result = [];
1350 return this._map.keysArray(); 1265 var keys = this.keysArray();
1351 }, 1266 for (var i = 0; i < keys.length; ++i)
1352 1267 result.pushAll(this.get(keys[i]).valuesArray());
1353 /** 1268 return result;
1354 * @return {!Array.<!V>} 1269 },
1355 */ 1270
1356 valuesArray: function() 1271 clear: function() {
1357 { 1272 this._map.clear();
1358 var result = []; 1273 }
1359 var keys = this.keysArray();
1360 for (var i = 0; i < keys.length; ++i)
1361 result.pushAll(this.get(keys[i]).valuesArray());
1362 return result;
1363 },
1364
1365 clear: function()
1366 {
1367 this._map.clear();
1368 }
1369 }; 1274 };
1370 1275
1371 /** 1276 /**
1372 * @param {string} url 1277 * @param {string} url
1373 * @return {!Promise.<string>} 1278 * @return {!Promise.<string>}
1374 */ 1279 */
1375 function loadXHR(url) 1280 function loadXHR(url) {
1376 { 1281 return new Promise(load);
1377 return new Promise(load); 1282
1378 1283 function load(successCallback, failureCallback) {
1379 function load(successCallback, failureCallback) 1284 function onReadyStateChanged() {
1380 { 1285 if (xhr.readyState !== XMLHttpRequest.DONE)
1381 function onReadyStateChanged() 1286 return;
1382 { 1287 if (xhr.status !== 200) {
1383 if (xhr.readyState !== XMLHttpRequest.DONE) 1288 xhr.onreadystatechange = null;
1384 return; 1289 failureCallback(new Error(xhr.status));
1385 if (xhr.status !== 200) { 1290 return;
1386 xhr.onreadystatechange = null; 1291 }
1387 failureCallback(new Error(xhr.status)); 1292 xhr.onreadystatechange = null;
1388 return; 1293 successCallback(xhr.responseText);
1389 } 1294 }
1390 xhr.onreadystatechange = null; 1295
1391 successCallback(xhr.responseText); 1296 var xhr = new XMLHttpRequest();
1392 } 1297 xhr.withCredentials = false;
1393 1298 xhr.open('GET', url, true);
1394 var xhr = new XMLHttpRequest(); 1299 xhr.onreadystatechange = onReadyStateChanged;
1395 xhr.withCredentials = false; 1300 xhr.send(null);
1396 xhr.open("GET", url, true); 1301 }
1397 xhr.onreadystatechange = onReadyStateChanged;
1398 xhr.send(null);
1399 }
1400 } 1302 }
1401 1303
1402 /** 1304 /**
1403 * @constructor 1305 * @unrestricted
1404 */ 1306 */
1405 function CallbackBarrier() 1307 var CallbackBarrier = class {
1406 { 1308 constructor() {
1407 this._pendingIncomingCallbacksCount = 0; 1309 this._pendingIncomingCallbacksCount = 0;
1408 } 1310 }
1409 1311
1410 CallbackBarrier.prototype = { 1312 /**
1313 * @param {function(...)=} userCallback
1314 * @return {function(...)}
1315 */
1316 createCallback(userCallback) {
1317 console.assert(
1318 !this._outgoingCallback, 'CallbackBarrier.createCallback() is called aft er CallbackBarrier.callWhenDone()');
1319 ++this._pendingIncomingCallbacksCount;
1320 return this._incomingCallback.bind(this, userCallback);
1321 }
1322
1323 /**
1324 * @param {function()} callback
1325 */
1326 callWhenDone(callback) {
1327 console.assert(!this._outgoingCallback, 'CallbackBarrier.callWhenDone() is c alled multiple times');
1328 this._outgoingCallback = callback;
1329 if (!this._pendingIncomingCallbacksCount)
1330 this._outgoingCallback();
1331 }
1332
1333 /**
1334 * @return {!Promise.<undefined>}
1335 */
1336 donePromise() {
1337 return new Promise(promiseConstructor.bind(this));
1338
1411 /** 1339 /**
1412 * @param {function(...)=} userCallback 1340 * @param {function()} success
1413 * @return {function(...)} 1341 * @this {CallbackBarrier}
1414 */ 1342 */
1415 createCallback: function(userCallback) 1343 function promiseConstructor(success) {
1416 { 1344 this.callWhenDone(success);
1417 console.assert(!this._outgoingCallback, "CallbackBarrier.createCallback( ) is called after CallbackBarrier.callWhenDone()"); 1345 }
1418 ++this._pendingIncomingCallbacksCount; 1346 }
1419 return this._incomingCallback.bind(this, userCallback); 1347
1420 }, 1348 /**
1421 1349 * @param {function(...)=} userCallback
1422 /** 1350 */
1423 * @param {function()} callback 1351 _incomingCallback(userCallback) {
1424 */ 1352 console.assert(this._pendingIncomingCallbacksCount > 0);
1425 callWhenDone: function(callback) 1353 if (userCallback) {
1426 { 1354 var args = Array.prototype.slice.call(arguments, 1);
1427 console.assert(!this._outgoingCallback, "CallbackBarrier.callWhenDone() is called multiple times"); 1355 userCallback.apply(null, args);
1428 this._outgoingCallback = callback; 1356 }
1429 if (!this._pendingIncomingCallbacksCount) 1357 if (!--this._pendingIncomingCallbacksCount && this._outgoingCallback)
1430 this._outgoingCallback(); 1358 this._outgoingCallback();
1431 }, 1359 }
1432
1433 /**
1434 * @return {!Promise.<undefined>}
1435 */
1436 donePromise: function()
1437 {
1438 return new Promise(promiseConstructor.bind(this));
1439
1440 /**
1441 * @param {function()} success
1442 * @this {CallbackBarrier}
1443 */
1444 function promiseConstructor(success)
1445 {
1446 this.callWhenDone(success);
1447 }
1448 },
1449
1450 /**
1451 * @param {function(...)=} userCallback
1452 */
1453 _incomingCallback: function(userCallback)
1454 {
1455 console.assert(this._pendingIncomingCallbacksCount > 0);
1456 if (userCallback) {
1457 var args = Array.prototype.slice.call(arguments, 1);
1458 userCallback.apply(null, args);
1459 }
1460 if (!--this._pendingIncomingCallbacksCount && this._outgoingCallback)
1461 this._outgoingCallback();
1462 }
1463 }; 1360 };
1464 1361
1465 /** 1362 /**
1466 * @param {*} value 1363 * @param {*} value
1467 */ 1364 */
1468 function suppressUnused(value) 1365 function suppressUnused(value) {
1469 {
1470 } 1366 }
1471 1367
1472 /** 1368 /**
1473 * @param {function()} callback 1369 * @param {function()} callback
1474 * @return {number} 1370 * @return {number}
1475 */ 1371 */
1476 self.setImmediate = function(callback) 1372 self.setImmediate = function(callback) {
1477 { 1373 Promise.resolve().then(callback);
1478 Promise.resolve().then(callback); 1374 return 0;
1479 return 0; 1375 };
1480 }; 1376
1481 1377 /**
1482 /**
1483 * @param {function(...?)} callback 1378 * @param {function(...?)} callback
1484 * @return {!Promise.<T>} 1379 * @return {!Promise.<T>}
1485 * @template T 1380 * @template T
1486 */ 1381 */
1487 Promise.prototype.spread = function(callback) 1382 Promise.prototype.spread = function(callback) {
1488 { 1383 return this.then(spreadPromise);
1489 return this.then(spreadPromise);
1490 1384
1491 function spreadPromise(arg) 1385 function spreadPromise(arg) {
1492 { 1386 return callback.apply(null, arg);
1493 return callback.apply(null, arg); 1387 }
1494 }
1495 }; 1388 };
1496 1389
1497 /** 1390 /**
1498 * @param {T} defaultValue 1391 * @param {T} defaultValue
1499 * @return {!Promise.<T>} 1392 * @return {!Promise.<T>}
1500 * @template T 1393 * @template T
1501 */ 1394 */
1502 Promise.prototype.catchException = function(defaultValue) { 1395 Promise.prototype.catchException = function(defaultValue) {
1503 return this.catch(function(error) { 1396 return this.catch(function(error) {
1504 console.error(error); 1397 console.error(error);
1505 return defaultValue; 1398 return defaultValue;
1506 }); 1399 });
1507 }; 1400 };
1508 1401
1509 /** 1402 /**
1510 * @param {!Map<number, ?>} other 1403 * @param {!Map<number, ?>} other
1511 * @param {function(!VALUE,?):boolean} isEqual 1404 * @param {function(!VALUE,?):boolean} isEqual
1512 * @return {!{removed: !Array<!VALUE>, added: !Array<?>, equal: !Array<!VALUE>}} 1405 * @return {!{removed: !Array<!VALUE>, added: !Array<?>, equal: !Array<!VALUE>}}
1513 * @this {Map<number, VALUE>} 1406 * @this {Map<number, VALUE>}
1514 */ 1407 */
1515 Map.prototype.diff = function(other, isEqual) 1408 Map.prototype.diff = function(other, isEqual) {
1516 { 1409 var leftKeys = this.keysArray();
1517 var leftKeys = this.keysArray(); 1410 var rightKeys = other.keysArray();
1518 var rightKeys = other.keysArray(); 1411 leftKeys.sort((a, b) => a - b);
1519 leftKeys.sort((a, b) => a - b); 1412 rightKeys.sort((a, b) => a - b);
1520 rightKeys.sort((a, b) => a - b);
1521 1413
1522 var removed = []; 1414 var removed = [];
1523 var added = []; 1415 var added = [];
1524 var equal = []; 1416 var equal = [];
1525 var leftIndex = 0; 1417 var leftIndex = 0;
1526 var rightIndex = 0; 1418 var rightIndex = 0;
1527 while (leftIndex < leftKeys.length && rightIndex < rightKeys.length) { 1419 while (leftIndex < leftKeys.length && rightIndex < rightKeys.length) {
1528 var leftKey = leftKeys[leftIndex]; 1420 var leftKey = leftKeys[leftIndex];
1529 var rightKey = rightKeys[rightIndex]; 1421 var rightKey = rightKeys[rightIndex];
1530 if (leftKey === rightKey && isEqual(this.get(leftKey), other.get(rightKe y))) { 1422 if (leftKey === rightKey && isEqual(this.get(leftKey), other.get(rightKey))) {
1531 equal.push(this.get(leftKey)); 1423 equal.push(this.get(leftKey));
1532 ++leftIndex; 1424 ++leftIndex;
1533 ++rightIndex; 1425 ++rightIndex;
1534 continue; 1426 continue;
1535 }
1536 if (leftKey <= rightKey) {
1537 removed.push(this.get(leftKey));
1538 ++leftIndex;
1539 continue;
1540 }
1541 added.push(other.get(rightKey));
1542 ++rightIndex;
1543 } 1427 }
1544 while (leftIndex < leftKeys.length) { 1428 if (leftKey <= rightKey) {
1545 var leftKey = leftKeys[leftIndex++]; 1429 removed.push(this.get(leftKey));
1546 removed.push(this.get(leftKey)); 1430 ++leftIndex;
1431 continue;
1547 } 1432 }
1548 while (rightIndex < rightKeys.length) { 1433 added.push(other.get(rightKey));
1549 var rightKey = rightKeys[rightIndex++]; 1434 ++rightIndex;
1550 added.push(other.get(rightKey)); 1435 }
1551 } 1436 while (leftIndex < leftKeys.length) {
1552 return { 1437 var leftKey = leftKeys[leftIndex++];
1553 added: added, 1438 removed.push(this.get(leftKey));
1554 removed: removed, 1439 }
1555 equal: equal 1440 while (rightIndex < rightKeys.length) {
1556 }; 1441 var rightKey = rightKeys[rightIndex++];
1442 added.push(other.get(rightKey));
1443 }
1444 return {added: added, removed: removed, equal: equal};
1557 }; 1445 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698