2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2012 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
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
16 * from this software without specific prior written permission.
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
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
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
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
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
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * @param {!Object} obj
34 Object.isEmpty = function(obj)
42 * @param {!Object.<string,!T>} obj
43 * @return {!Array.<!T>}
46 Object.values = function(obj)
48 var result = Object.keys(obj);
49 var length = result.length;
50 for (var i = 0; i < length; ++i)
51 result[i] = obj[result[i]];
56 * @param {string} string
57 * @return {!Array.<number>}
59 String.prototype.findAll = function(string)
62 var i = this.indexOf(string);
65 i = this.indexOf(string, i + string.length);
71 * @return {!Array.<number>}
73 String.prototype.lineEndings = function()
75 if (!this._lineEndings) {
76 this._lineEndings = this.findAll("\n");
77 this._lineEndings.push(this.length);
79 return this._lineEndings;
85 String.prototype.lineCount = function()
87 var lineEndings = this.lineEndings();
88 return lineEndings.length;
94 String.prototype.lineAt = function(lineNumber)
96 var lineEndings = this.lineEndings();
97 var lineStart = lineNumber > 0 ? lineEndings[lineNumber - 1] + 1 : 0;
98 var lineEnd = lineEndings[lineNumber];
99 var lineContent = this.substring(lineStart, lineEnd);
100 if (lineContent.length > 0 && lineContent.charAt(lineContent.length - 1) === "\r")
101 lineContent = lineContent.substring(0, lineContent.length - 1);
106 * @param {string} chars
109 String.prototype.escapeCharacters = function(chars)
111 var foundChar = false;
112 for (var i = 0; i < chars.length; ++i) {
113 if (this.indexOf(chars.charAt(i)) !== -1) {
123 for (var i = 0; i < this.length; ++i) {
124 if (chars.indexOf(this.charAt(i)) !== -1)
126 result += this.charAt(i);
135 String.regexSpecialCharacters = function()
137 return "^[]{}()\\.^$*+?|-,";
143 String.prototype.escapeForRegExp = function()
145 return this.escapeCharacters(String.regexSpecialCharacters());
151 String.prototype.escapeHTML = function()
153 return this.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """); //" doublequotes just for editor
159 String.prototype.collapseWhitespace = function()
161 return this.replace(/[\s\xA0]+/g, " ");
165 * @param {number} maxLength
168 String.prototype.trimMiddle = function(maxLength)
170 if (this.length <= maxLength)
172 var leftHalf = maxLength >> 1;
173 var rightHalf = maxLength - leftHalf - 1;
174 return this.substr(0, leftHalf) + "\u2026" + this.substr(this.length - rightHalf, rightHalf);
178 * @param {number} maxLength
181 String.prototype.trimEnd = function(maxLength)
183 if (this.length <= maxLength)
185 return this.substr(0, maxLength - 1) + "\u2026";
189 * @param {?string=} baseURLDomain
192 String.prototype.trimURL = function(baseURLDomain)
194 var result = this.replace(/^(https|http|file):\/\//i, "");
196 result = result.replace(new RegExp("^" + baseURLDomain.escapeForRegExp(), "i"), "");
203 String.prototype.toTitleCase = function()
205 return this.substring(0, 1).toUpperCase() + this.substring(1);
209 * @param {string} other
212 String.prototype.compareTo = function(other)
222 * @param {string} href
225 function sanitizeHref(href)
227 return href && href.trim().toLowerCase().startsWith("javascript:") ? null : href;
233 String.prototype.removeURLFragment = function()
235 var fragmentIndex = this.indexOf("#");
236 if (fragmentIndex == -1)
237 fragmentIndex = this.length;
238 return this.substring(0, fragmentIndex);
244 String.prototype.startsWith = function(substring)
246 return !this.lastIndexOf(substring, 0);
252 String.prototype.endsWith = function(substring)
254 return this.indexOf(substring, this.length - substring.length) !== -1;
260 String.prototype.hashCode = function()
263 for (var i = 0; i < this.length; ++i)
264 result = result * 3 + this.charCodeAt(i);
273 String.naturalOrderComparator = function(a, b)
275 var chunk = /^\d+|^\D+/;
276 var chunka, chunkb, anum, bnum;
287 chunka = a.match(chunk)[0];
288 chunkb = b.match(chunk)[0];
289 anum = !isNaN(chunka);
290 bnum = !isNaN(chunkb);
296 var diff = chunka - chunkb;
299 if (chunka.length !== chunkb.length) {
300 if (!+chunka && !+chunkb) // chunks are strings of all 0s (special case)
301 return chunka.length - chunkb.length;
303 return chunkb.length - chunka.length;
305 } else if (chunka !== chunkb)
306 return (chunka < chunkb) ? -1 : 1;
307 a = a.substring(chunka.length);
308 b = b.substring(chunkb.length);
313 * @param {number} num
314 * @param {number} min
315 * @param {number} max
318 Number.constrain = function(num, min, max)
332 Number.gcd = function(a, b)
337 return Number.gcd(b, a % b);
341 * @param {string} value
344 Number.toFixedIfFloating = function(value)
346 if (!value || isNaN(value))
348 var number = Number(value);
349 return number % 1 ? number.toFixed(3) : String(number);
355 Date.prototype.toISO8601Compact = function()
363 return (x > 9 ? "" : "0") + x;
365 return this.getFullYear() +
366 leadZero(this.getMonth() + 1) +
367 leadZero(this.getDate()) + "T" +
368 leadZero(this.getHours()) +
369 leadZero(this.getMinutes()) +
370 leadZero(this.getSeconds());
376 Date.prototype.toConsoleTime = function()
382 function leadZero2(x)
384 return (x > 9 ? "" : "0") + x;
391 function leadZero3(x)
393 return (Array(4 - x.toString().length)).join('0') + x;
396 return this.getFullYear() + "-" +
397 leadZero2(this.getMonth() + 1) + "-" +
398 leadZero2(this.getDate()) + " " +
399 leadZero2(this.getHours()) + ":" +
400 leadZero2(this.getMinutes()) + ":" +
401 leadZero2(this.getSeconds()) + "." +
402 leadZero3(this.getMilliseconds());
405 Object.defineProperty(Array.prototype, "remove",
409 * @param {boolean=} firstOnly
413 value: function(value, firstOnly)
415 var index = this.indexOf(value);
419 this.splice(index, 1);
422 for (var i = index + 1, n = this.length; i < n; ++i) {
423 if (this[i] !== value)
424 this[index++] = this[i];
430 Object.defineProperty(Array.prototype, "keySet",
433 * @return {!Object.<string, boolean>}
439 for (var i = 0; i < this.length; ++i)
440 keys[this[i]] = true;
445 Object.defineProperty(Array.prototype, "rotate",
448 * @param {number} index
449 * @return {!Array.<!T>}
453 value: function(index)
456 for (var i = index; i < index + this.length; ++i)
457 result.push(this[i % this.length]);
462 Object.defineProperty(Uint32Array.prototype, "sort", {
463 value: Array.prototype.sort
469 * @this {Array.<number>}
470 * @param {function(number, number): number} comparator
471 * @param {number} left
472 * @param {number} right
473 * @param {number} pivotIndex
475 value: function(comparator, left, right, pivotIndex)
477 function swap(array, i1, i2)
479 var temp = array[i1];
480 array[i1] = array[i2];
484 var pivotValue = this[pivotIndex];
485 swap(this, right, pivotIndex);
486 var storeIndex = left;
487 for (var i = left; i < right; ++i) {
488 if (comparator(this[i], pivotValue) < 0) {
489 swap(this, storeIndex, i);
493 swap(this, right, storeIndex);
497 Object.defineProperty(Array.prototype, "partition", partition);
498 Object.defineProperty(Uint32Array.prototype, "partition", partition);
502 * @param {function(number, number): number} comparator
503 * @param {number} leftBound
504 * @param {number} rightBound
505 * @param {number} sortWindowLeft
506 * @param {number} sortWindowRight
507 * @return {!Array.<number>}
508 * @this {Array.<number>}
510 value: function(comparator, leftBound, rightBound, sortWindowLeft, sortWindowRight)
512 function quickSortRange(array, comparator, left, right, sortWindowLeft, sortWindowRight)
516 var pivotIndex = Math.floor(Math.random() * (right - left)) + left;
517 var pivotNewIndex = array.partition(comparator, left, right, pivotIndex);
518 if (sortWindowLeft < pivotNewIndex)
519 quickSortRange(array, comparator, left, pivotNewIndex - 1, sortWindowLeft, sortWindowRight);
520 if (pivotNewIndex < sortWindowRight)
521 quickSortRange(array, comparator, pivotNewIndex + 1, right, sortWindowLeft, sortWindowRight);
523 if (leftBound === 0 && rightBound === (this.length - 1) && sortWindowLeft === 0 && sortWindowRight >= rightBound)
524 this.sort(comparator);
526 quickSortRange(this, comparator, leftBound, rightBound, sortWindowLeft, sortWindowRight);
530 Object.defineProperty(Array.prototype, "sortRange", sortRange);
531 Object.defineProperty(Uint32Array.prototype, "sortRange", sortRange);
534 Object.defineProperty(Array.prototype, "stableSort",
537 * @param {function(?T, ?T): number=} comparator
538 * @return {!Array.<?T>}
542 value: function(comparator)
544 function defaultComparator(a, b)
546 return a < b ? -1 : (a > b ? 1 : 0);
548 comparator = comparator || defaultComparator;
550 var indices = new Array(this.length);
551 for (var i = 0; i < this.length; ++i)
559 function indexComparator(a, b)
561 var result = comparator(self[a], self[b]);
562 return result ? result : a - b;
564 indices.sort(indexComparator);
566 for (var i = 0; i < this.length; ++i) {
567 if (indices[i] < 0 || i === indices[i])
572 var next = indices[cyclical];
573 indices[cyclical] = -1;
575 this[cyclical] = saved;
578 this[cyclical] = this[next];
587 Object.defineProperty(Array.prototype, "qselect",
591 * @param {function(number, number): number=} comparator
592 * @return {number|undefined}
593 * @this {Array.<number>}
595 value: function(k, comparator)
597 if (k < 0 || k >= this.length)
600 comparator = function(a, b) { return a - b; }
603 var high = this.length - 1;
605 var pivotPosition = this.partition(comparator, low, high, Math.floor((high + low) / 2));
606 if (pivotPosition === k)
608 else if (pivotPosition > k)
609 high = pivotPosition - 1;
611 low = pivotPosition + 1;
616 Object.defineProperty(Array.prototype, "lowerBound",
619 * Return index of the leftmost element that is equal or greater
620 * than the specimen object. If there's no such element (i.e. all
621 * elements are smaller than the specimen) returns right bound.
622 * The function works for sorted array.
623 * When specified, |left| (inclusive) and |right| (exclusive) indices
624 * define the search window.
627 * @param {function(!T,!S):number=} comparator
628 * @param {number=} left
629 * @param {number=} right
634 value: function(object, comparator, left, right)
636 function defaultComparator(a, b)
638 return a < b ? -1 : (a > b ? 1 : 0);
640 comparator = comparator || defaultComparator;
642 var r = right !== undefined ? right : this.length;
644 var m = (l + r) >> 1;
645 if (comparator(object, this[m]) > 0)
654 Object.defineProperty(Array.prototype, "upperBound",
657 * Return index of the leftmost element that is greater
658 * than the specimen object. If there's no such element (i.e. all
659 * elements are smaller or equal to the specimen) returns right bound.
660 * The function works for sorted array.
661 * When specified, |left| (inclusive) and |right| (exclusive) indices
662 * define the search window.
665 * @param {function(!T,!S):number=} comparator
666 * @param {number=} left
667 * @param {number=} right
672 value: function(object, comparator, left, right)
674 function defaultComparator(a, b)
676 return a < b ? -1 : (a > b ? 1 : 0);
678 comparator = comparator || defaultComparator;
680 var r = right !== undefined ? right : this.length;
682 var m = (l + r) >> 1;
683 if (comparator(object, this[m]) >= 0)
692 Object.defineProperty(Array.prototype, "binaryIndexOf",
696 * @param {function(!T,!S):number} comparator
701 value: function(value, comparator)
703 var index = this.lowerBound(value, comparator);
704 return index < this.length && comparator(value, this[index]) === 0 ? index : -1;
708 Object.defineProperty(Array.prototype, "select",
711 * @param {string} field
712 * @return {!Array.<!T>}
713 * @this {Array.<!Object.<string,!T>>}
716 value: function(field)
718 var result = new Array(this.length);
719 for (var i = 0; i < this.length; ++i)
720 result[i] = this[i][field];
725 Object.defineProperty(Array.prototype, "peekLast",
728 * @return {!T|undefined}
734 return this[this.length - 1];
741 * @param {!Array.<T>} array1
742 * @param {!Array.<T>} array2
743 * @param {function(T,T):number} comparator
744 * @return {!Array.<T>}
747 function mergeOrIntersect(array1, array2, comparator, mergeNotIntersect)
752 while (i < array1.length && j < array2.length) {
753 var compareValue = comparator(array1[i], array2[j]);
754 if (mergeNotIntersect || !compareValue)
755 result.push(compareValue <= 0 ? array1[i] : array2[j]);
756 if (compareValue <= 0)
758 if (compareValue >= 0)
761 if (mergeNotIntersect) {
762 while (i < array1.length)
763 result.push(array1[i++]);
764 while (j < array2.length)
765 result.push(array2[j++]);
770 Object.defineProperty(Array.prototype, "intersectOrdered",
773 * @param {!Array.<T>} array
774 * @param {function(T,T):number} comparator
775 * @return {!Array.<T>}
779 value: function(array, comparator)
781 return mergeOrIntersect(this, array, comparator, false);
785 Object.defineProperty(Array.prototype, "mergeOrdered",
788 * @param {!Array.<T>} array
789 * @param {function(T,T):number} comparator
790 * @return {!Array.<T>}
794 value: function(array, comparator)
796 return mergeOrIntersect(this, array, comparator, true);
805 * @param {!Array.<!S>} list
806 * @param {function(!T,!S):number=} comparator
807 * @param {boolean=} insertionIndexAfter
811 function insertionIndexForObjectInListSortedByFunction(object, list, comparator, insertionIndexAfter)
813 if (insertionIndexAfter)
814 return list.upperBound(object, comparator);
816 return list.lowerBound(object, comparator);
820 * @param {string} format
821 * @param {...*} var_arg
824 String.sprintf = function(format, var_arg)
826 return String.vsprintf(format, Array.prototype.slice.call(arguments, 1));
829 String.tokenizeFormatString = function(format, formatters)
832 var substitutionIndex = 0;
834 function addStringToken(str)
836 tokens.push({ type: "string", value: str });
839 function addSpecifierToken(specifier, precision, substitutionIndex)
841 tokens.push({ type: "specifier", specifier: specifier, precision: precision, substitutionIndex: substitutionIndex });
846 return !!/[0-9]/.exec(c);
850 for (var precentIndex = format.indexOf("%", index); precentIndex !== -1; precentIndex = format.indexOf("%", index)) {
851 addStringToken(format.substring(index, precentIndex));
852 index = precentIndex + 1;
854 if (isDigit(format[index])) {
855 // The first character is a number, it might be a substitution index.
856 var number = parseInt(format.substring(index), 10);
857 while (isDigit(format[index]))
860 // If the number is greater than zero and ends with a "$",
861 // then this is a substitution index.
862 if (number > 0 && format[index] === "$") {
863 substitutionIndex = (number - 1);
869 if (format[index] === ".") {
870 // This is a precision specifier. If no digit follows the ".",
871 // then the precision should be zero.
873 precision = parseInt(format.substring(index), 10);
874 if (isNaN(precision))
877 while (isDigit(format[index]))
881 if (!(format[index] in formatters)) {
882 addStringToken(format.substring(precentIndex, index + 1));
887 addSpecifierToken(format[index], precision, substitutionIndex);
893 addStringToken(format.substring(index));
898 String.standardFormatters = {
902 d: function(substitution)
904 return !isNaN(substitution) ? substitution : 0;
910 f: function(substitution, token)
912 if (substitution && token.precision > -1)
913 substitution = substitution.toFixed(token.precision);
914 return !isNaN(substitution) ? substitution : (token.precision > -1 ? Number(0).toFixed(token.precision) : 0);
920 s: function(substitution)
927 * @param {string} format
928 * @param {!Array.<*>} substitutions
931 String.vsprintf = function(format, substitutions)
933 return String.format(format, substitutions, String.standardFormatters, "", function(a, b) { return a + b; }).formattedResult;
936 String.format = function(format, substitutions, formatters, initialValue, append)
938 if (!format || !substitutions || !substitutions.length)
939 return { formattedResult: append(initialValue, format), unusedSubstitutions: substitutions };
941 function prettyFunctionName()
943 return "String.format(\"" + format + "\", \"" + substitutions.join("\", \"") + "\")";
948 console.warn(prettyFunctionName() + ": " + msg);
953 console.error(prettyFunctionName() + ": " + msg);
956 var result = initialValue;
957 var tokens = String.tokenizeFormatString(format, formatters);
958 var usedSubstitutionIndexes = {};
960 for (var i = 0; i < tokens.length; ++i) {
961 var token = tokens[i];
963 if (token.type === "string") {
964 result = append(result, token.value);
968 if (token.type !== "specifier") {
969 error("Unknown token type \"" + token.type + "\" found.");
973 if (token.substitutionIndex >= substitutions.length) {
974 // If there are not enough substitutions for the current substitutionIndex
975 // just output the format specifier literally and move on.
976 error("not enough substitution arguments. Had " + substitutions.length + " but needed " + (token.substitutionIndex + 1) + ", so substitution was skipped.");
977 result = append(result, "%" + (token.precision > -1 ? token.precision : "") + token.specifier);
981 usedSubstitutionIndexes[token.substitutionIndex] = true;
983 if (!(token.specifier in formatters)) {
984 // Encountered an unsupported format character, treat as a string.
985 warn("unsupported format character \u201C" + token.specifier + "\u201D. Treating as a string.");
986 result = append(result, substitutions[token.substitutionIndex]);
990 result = append(result, formatters[token.specifier](substitutions[token.substitutionIndex], token));
993 var unusedSubstitutions = [];
994 for (var i = 0; i < substitutions.length; ++i) {
995 if (i in usedSubstitutionIndexes)
997 unusedSubstitutions.push(substitutions[i]);
1000 return { formattedResult: result, unusedSubstitutions: unusedSubstitutions };
1004 * @param {string} query
1005 * @param {boolean} caseSensitive
1006 * @param {boolean} isRegex
1009 function createSearchRegex(query, caseSensitive, isRegex)
1011 var regexFlags = caseSensitive ? "g" : "gi";
1016 regexObject = new RegExp(query, regexFlags);
1023 regexObject = createPlainTextSearchRegex(query, regexFlags);
1029 * @param {string} query
1030 * @param {string=} flags
1033 function createPlainTextSearchRegex(query, flags)
1035 // This should be kept the same as the one in ContentSearchUtils.cpp.
1036 var regexSpecialCharacters = String.regexSpecialCharacters();
1038 for (var i = 0; i < query.length; ++i) {
1039 var c = query.charAt(i);
1040 if (regexSpecialCharacters.indexOf(c) != -1)
1044 return new RegExp(regex, flags || "");
1048 * @param {!RegExp} regex
1049 * @param {string} content
1052 function countRegexMatches(regex, content)
1057 while (text && (match = regex.exec(text))) {
1058 if (match[0].length > 0)
1060 text = text.substring(match.index + 1);
1066 * @param {number} value
1067 * @param {number} symbolsCount
1070 function numberToStringWithSpacesPadding(value, symbolsCount)
1072 var numberString = value.toString();
1073 var paddingLength = Math.max(0, symbolsCount - numberString.length);
1074 var paddingString = Array(paddingLength + 1).join("\u00a0");
1075 return paddingString + numberString;
1081 var createObjectIdentifier = function()
1083 // It has to be string for better performance.
1084 return "_" + ++createObjectIdentifier._last;
1087 createObjectIdentifier._last = 0;
1093 var Set = function()
1095 /** @type {!Object.<string, !T>} */
1106 var objectIdentifier = item.__identifier;
1107 if (!objectIdentifier) {
1108 objectIdentifier = createObjectIdentifier();
1109 item.__identifier = objectIdentifier;
1111 if (!this._set[objectIdentifier])
1113 this._set[objectIdentifier] = item;
1120 remove: function(item)
1122 if (this._set[item.__identifier]) {
1124 delete this._set[item.__identifier];
1131 * @return {!Array.<!T>}
1135 var result = new Array(this._size);
1137 for (var objectIdentifier in this._set)
1138 result[i++] = this._set[objectIdentifier];
1146 hasItem: function(item)
1148 return !!this._set[item.__identifier];
1170 var Map = function()
1172 /** @type {!Object.<string, !Array.<K|V>>} */
1182 put: function(key, value)
1184 var objectIdentifier = key.__identifier;
1185 if (!objectIdentifier) {
1186 objectIdentifier = createObjectIdentifier();
1187 key.__identifier = objectIdentifier;
1189 if (!this._map[objectIdentifier])
1191 this._map[objectIdentifier] = [key, value];
1198 remove: function(key)
1200 var result = this._map[key.__identifier];
1204 delete this._map[key.__identifier];
1209 * @return {!Array.<K>}
1213 return this._list(0);
1217 * @return {!Array.<V>}
1221 return this._list(1);
1225 * @param {number} index
1226 * @return {!Array.<K|V>}
1228 _list: function(index)
1230 var result = new Array(this._size);
1232 for (var objectIdentifier in this._map)
1233 result[i++] = this._map[objectIdentifier][index];
1239 * @return {V|undefined}
1243 var entry = this._map[key.__identifier];
1244 return entry ? entry[1] : undefined;
1251 contains: function(key)
1253 var entry = this._map[key.__identifier];
1276 var StringMap = function()
1278 /** @type {!Object.<string, T>} */
1283 StringMap.prototype = {
1285 * @param {string} key
1288 put: function(key, value)
1290 if (key === "__proto__") {
1291 if (!this._hasProtoKey) {
1293 this._hasProtoKey = true;
1296 this._protoValue = value;
1299 if (!Object.prototype.hasOwnProperty.call(this._map, key))
1301 this._map[key] = value;
1305 * @param {string} key
1306 * @return {T|undefined}
1308 remove: function(key)
1311 if (key === "__proto__") {
1312 if (!this._hasProtoKey)
1315 delete this._hasProtoKey;
1316 result = this._protoValue;
1317 delete this._protoValue;
1320 if (!Object.prototype.hasOwnProperty.call(this._map, key))
1323 result = this._map[key];
1324 delete this._map[key];
1329 * @return {!Array.<string>}
1333 var result = Object.keys(this._map) || [];
1334 if (this._hasProtoKey)
1335 result.push("__proto__");
1340 * @return {!Array.<T>}
1344 var result = Object.values(this._map);
1345 if (this._hasProtoKey)
1346 result.push(this._protoValue);
1351 * @param {string} key
1352 * @return {T|undefined}
1356 if (key === "__proto__")
1357 return this._protoValue;
1358 if (!Object.prototype.hasOwnProperty.call(this._map, key))
1360 return this._map[key];
1364 * @param {string} key
1367 contains: function(key)
1370 if (key === "__proto__")
1371 return this._hasProtoKey;
1372 return Object.prototype.hasOwnProperty.call(this._map, key);
1387 delete this._hasProtoKey;
1388 delete this._protoValue;
1395 var StringSet = function()
1397 /** @type {!StringMap.<boolean>} */
1398 this._map = new StringMap();
1401 StringSet.prototype = {
1403 * @param {string} value
1405 put: function(value)
1407 this._map.put(value, true);
1411 * @param {string} value
1414 remove: function(value)
1416 return !!this._map.remove(value);
1420 * @return {!Array.<string>}
1424 return this._map.keys();
1428 * @param {string} value
1431 contains: function(value)
1433 return this._map.contains(value);
1441 return this._map.size();
1451 * @param {string} url
1452 * @param {boolean=} async
1453 * @param {function(?string)=} callback
1456 function loadXHR(url, async, callback)
1458 function onReadyStateChanged()
1460 if (xhr.readyState !== XMLHttpRequest.DONE)
1463 if (xhr.status === 200) {
1464 callback(xhr.responseText);
1471 var xhr = new XMLHttpRequest();
1472 xhr.open("GET", url, async);
1474 xhr.onreadystatechange = onReadyStateChanged;
1478 if (xhr.status === 200)
1479 return xhr.responseText;
1485 var _importedScripts = {};
1488 * This function behavior depends on the "debug_devtools" flag value.
1489 * - In debug mode it loads scripts synchronously via xhr request.
1490 * - In release mode every occurrence of "importScript" in the js files
1491 * that have been white listed in the build system gets replaced with
1492 * the script source code on the compilation phase.
1493 * The build system will throw an exception if it found importScript call
1496 * To load scripts lazily in release mode call "loadScript" function.
1497 * @param {string} scriptName
1499 function importScript(scriptName)
1501 if (_importedScripts[scriptName])
1503 var xhr = new XMLHttpRequest();
1504 _importedScripts[scriptName] = true;
1505 xhr.open("GET", scriptName, false);
1507 if (!xhr.responseText)
1508 throw "empty response arrived for script '" + scriptName + "'";
1509 var baseUrl = location.origin + location.pathname;
1510 baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf("/"));
1511 var sourceURL = baseUrl + "/" + scriptName;
1512 self.eval(xhr.responseText + "\n//# sourceURL=" + sourceURL);
1515 var loadScript = importScript;
1520 function CallbackBarrier()
1522 this._pendingIncomingCallbacksCount = 0;
1525 CallbackBarrier.prototype = {
1527 * @param {function(!T)=} userCallback
1528 * @return {function(!T=)}
1531 createCallback: function(userCallback)
1533 console.assert(!this._outgoingCallback, "CallbackBarrier.createCallback() is called after CallbackBarrier.callWhenDone()");
1534 ++this._pendingIncomingCallbacksCount;
1535 return this._incomingCallback.bind(this, userCallback);
1539 * @param {function()} callback
1541 callWhenDone: function(callback)
1543 console.assert(!this._outgoingCallback, "CallbackBarrier.callWhenDone() is called multiple times");
1544 this._outgoingCallback = callback;
1545 if (!this._pendingIncomingCallbacksCount)
1546 this._outgoingCallback();
1550 * @param {function(...)=} userCallback
1552 _incomingCallback: function(userCallback)
1554 console.assert(this._pendingIncomingCallbacksCount > 0);
1556 var args = Array.prototype.slice.call(arguments, 1);
1557 userCallback.apply(null, args);
1559 if (!--this._pendingIncomingCallbacksCount && this._outgoingCallback)
1560 this._outgoingCallback();
1567 function suppressUnused(value)