1/*
2 * Copyright (C) 2007 Apple Inc.  All rights reserved.
3 * Copyright (C) 2012 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
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.
17 *
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.
28 */
29
30Object.isEmpty = function(obj)
31{
32    for (var i in obj)
33        return false;
34    return true;
35}
36
37Object.values = function(obj)
38{
39    var keys = Object.keys(obj);
40    var result = [];
41
42    for (var i = 0; i < keys.length; ++i)
43        result.push(obj[keys[i]]);
44    return result;
45}
46
47String.prototype.hasSubstring = function(string, caseInsensitive)
48{
49    if (!caseInsensitive)
50        return this.indexOf(string) !== -1;
51    return this.match(new RegExp(string.escapeForRegExp(), "i"));
52}
53
54String.prototype.findAll = function(string)
55{
56    var matches = [];
57    var i = this.indexOf(string);
58    while (i !== -1) {
59        matches.push(i);
60        i = this.indexOf(string, i + string.length);
61    }
62    return matches;
63}
64
65String.prototype.lineEndings = function()
66{
67    if (!this._lineEndings) {
68        this._lineEndings = this.findAll("\n");
69        this._lineEndings.push(this.length);
70    }
71    return this._lineEndings;
72}
73
74String.prototype.escapeCharacters = function(chars)
75{
76    var foundChar = false;
77    for (var i = 0; i < chars.length; ++i) {
78        if (this.indexOf(chars.charAt(i)) !== -1) {
79            foundChar = true;
80            break;
81        }
82    }
83
84    if (!foundChar)
85        return String(this);
86
87    var result = "";
88    for (var i = 0; i < this.length; ++i) {
89        if (chars.indexOf(this.charAt(i)) !== -1)
90            result += "\\";
91        result += this.charAt(i);
92    }
93
94    return result;
95}
96
97String.regexSpecialCharacters = function()
98{
99    return "^[]{}()\\.$*+?|-,";
100}
101
102String.prototype.escapeForRegExp = function()
103{
104    return this.escapeCharacters(String.regexSpecialCharacters);
105}
106
107String.prototype.escapeHTML = function()
108{
109    return this.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"); //" doublequotes just for editor
110}
111
112String.prototype.collapseWhitespace = function()
113{
114    return this.replace(/[\s\xA0]+/g, " ");
115}
116
117String.prototype.centerEllipsizedToLength = function(maxLength)
118{
119    if (this.length <= maxLength)
120        return String(this);
121    var leftHalf = maxLength >> 1;
122    var rightHalf = maxLength - leftHalf - 1;
123    return this.substr(0, leftHalf) + "\u2026" + this.substr(this.length - rightHalf, rightHalf);
124}
125
126String.prototype.trimEnd = function(maxLength)
127{
128    if (this.length <= maxLength)
129        return String(this);
130    return this.substr(0, maxLength - 1) + "\u2026";
131}
132
133String.prototype.trimURL = function(baseURLDomain)
134{
135    var result = this.replace(/^(https|http|file):\/\//i, "");
136    if (baseURLDomain)
137        result = result.replace(new RegExp("^" + baseURLDomain.escapeForRegExp(), "i"), "");
138    return result;
139}
140
141String.prototype.toTitleCase = function()
142{
143    return this.substring(0, 1).toUpperCase() + this.substring(1);
144}
145
146/**
147 * @param {string} other
148 * @return {number}
149 */
150String.prototype.compareTo = function(other)
151{
152    if (this > other)
153        return 1;
154    if (this < other)
155        return -1;
156    return 0;
157}
158
159/**
160 * @param {string} href
161 * @return {string}
162 */
163function sanitizeHref(href)
164{
165    return href && href.trim().toLowerCase().startsWith("javascript:") ? "" : href;
166}
167
168String.prototype.removeURLFragment = function()
169{
170    var fragmentIndex = this.indexOf("#");
171    if (fragmentIndex == -1)
172        fragmentIndex = this.length;
173    return this.substring(0, fragmentIndex);
174}
175
176String.prototype.startsWith = function(substring)
177{
178    return !this.lastIndexOf(substring, 0);
179}
180
181String.prototype.endsWith = function(substring)
182{
183    return this.indexOf(substring, this.length - substring.length) !== -1;
184}
185
186Number.constrain = function(num, min, max)
187{
188    if (num < min)
189        num = min;
190    else if (num > max)
191        num = max;
192    return num;
193}
194
195Date.prototype.toISO8601Compact = function()
196{
197    function leadZero(x)
198    {
199        return x > 9 ? '' + x : '0' + x
200    }
201    return this.getFullYear() +
202           leadZero(this.getMonth() + 1) +
203           leadZero(this.getDate()) + 'T' +
204           leadZero(this.getHours()) +
205           leadZero(this.getMinutes()) +
206           leadZero(this.getSeconds());
207}
208
209Object.defineProperty(Array.prototype, "remove",
210{
211    /**
212     * @this {Array.<*>}
213     */
214    value: function(value, onlyFirst)
215    {
216        if (onlyFirst) {
217            var index = this.indexOf(value);
218            if (index !== -1)
219                this.splice(index, 1);
220            return;
221        }
222
223        var length = this.length;
224        for (var i = 0; i < length; ++i) {
225            if (this[i] === value)
226                this.splice(i, 1);
227        }
228    }
229});
230
231Object.defineProperty(Array.prototype, "keySet",
232{
233    /**
234     * @this {Array.<*>}
235     */
236    value: function()
237    {
238        var keys = {};
239        for (var i = 0; i < this.length; ++i)
240            keys[this[i]] = true;
241        return keys;
242    }
243});
244
245Object.defineProperty(Array.prototype, "upperBound",
246{
247    /**
248     * @this {Array.<number>}
249     */
250    value: function(value)
251    {
252        var first = 0;
253        var count = this.length;
254        while (count > 0) {
255          var step = count >> 1;
256          var middle = first + step;
257          if (value >= this[middle]) {
258              first = middle + 1;
259              count -= step + 1;
260          } else
261              count = step;
262        }
263        return first;
264    }
265});
266
267Object.defineProperty(Array.prototype, "rotate",
268{
269    /**
270     * @this {Array.<*>}
271     * @param {number} index
272     * @return {Array.<*>}
273     */
274    value: function(index)
275    {
276        var result = [];
277        for (var i = index; i < index + this.length; ++i)
278            result.push(this[i % this.length]);
279        return result;
280    }
281});
282
283Object.defineProperty(Uint32Array.prototype, "sort", {
284   value: Array.prototype.sort
285});
286
287(function() {
288var partition = {
289    /**
290     * @this {Array.<number>}
291     * @param {function(number,number):number} comparator
292     * @param {number} left
293     * @param {number} right
294     * @param {number} pivotIndex
295     */
296    value: function(comparator, left, right, pivotIndex)
297    {
298        function swap(array, i1, i2)
299        {
300            var temp = array[i1];
301            array[i1] = array[i2];
302            array[i2] = temp;
303        }
304
305        var pivotValue = this[pivotIndex];
306        swap(this, right, pivotIndex);
307        var storeIndex = left;
308        for (var i = left; i < right; ++i) {
309            if (comparator(this[i], pivotValue) < 0) {
310                swap(this, storeIndex, i);
311                ++storeIndex;
312            }
313        }
314        swap(this, right, storeIndex);
315        return storeIndex;
316    }
317};
318Object.defineProperty(Array.prototype, "partition", partition);
319Object.defineProperty(Uint32Array.prototype, "partition", partition);
320
321var sortRange = {
322    /**
323     * @this {Array.<number>}
324     * @param {function(number,number):number} comparator
325     * @param {number} leftBound
326     * @param {number} rightBound
327     * @param {number} k
328     */
329    value: function(comparator, leftBound, rightBound, k)
330    {
331        function quickSortFirstK(array, comparator, left, right, k)
332        {
333            if (right <= left)
334                return;
335            var pivotIndex = Math.floor(Math.random() * (right - left)) + left;
336            var pivotNewIndex = array.partition(comparator, left, right, pivotIndex);
337            quickSortFirstK(array, comparator, left, pivotNewIndex - 1, k);
338            if (pivotNewIndex < left + k - 1)
339                quickSortFirstK(array, comparator, pivotNewIndex + 1, right, k);
340        }
341
342        if (leftBound === 0 && rightBound === (this.length - 1) && k >= this.length)
343            this.sort(comparator);
344        else
345            quickSortFirstK(this, comparator, leftBound, rightBound, k);
346        return this;
347    }
348}
349Object.defineProperty(Array.prototype, "sortRange", sortRange);
350Object.defineProperty(Uint32Array.prototype, "sortRange", sortRange);
351})();
352
353Object.defineProperty(Array.prototype, "qselect",
354{
355    /**
356     * @this {Array.<number>}
357     * @param {number} k
358     * @param {function(number,number):boolean=} comparator
359     */
360    value: function(k, comparator)
361    {
362        if (k < 0 || k >= this.length)
363            return;
364        if (!comparator)
365            comparator = function(a, b) { return a - b; }
366
367        var low = 0;
368        var high = this.length - 1;
369        for (;;) {
370            var pivotPosition = this.partition(comparator, low, high, Math.floor((high + low) / 2));
371            if (pivotPosition === k)
372                return this[k];
373            else if (pivotPosition > k)
374                high = pivotPosition - 1;
375            else
376                low = pivotPosition + 1;
377        }
378    }
379});
380
381/**
382 * @param {*} object
383 * @param {Array.<*>} array
384 * @param {function(*, *):number} comparator
385 */
386function binarySearch(object, array, comparator)
387{
388    var first = 0;
389    var last = array.length - 1;
390
391    while (first <= last) {
392        var mid = (first + last) >> 1;
393        var c = comparator(object, array[mid]);
394        if (c > 0)
395            first = mid + 1;
396        else if (c < 0)
397            last = mid - 1;
398        else
399            return mid;
400    }
401
402    // Return the nearest lesser index, "-1" means "0, "-2" means "1", etc.
403    return -(first + 1);
404}
405
406Object.defineProperty(Array.prototype, "binaryIndexOf",
407{
408    /**
409     * @this {Array.<*>}
410     * @param {function(*, *):number} comparator
411     */
412    value: function(value, comparator)
413    {
414        var result = binarySearch(value, this, comparator);
415        return result >= 0 ? result : -1;
416    }
417});
418
419Object.defineProperty(Array.prototype, "select",
420{
421    /**
422     * @this {Array.<*>}
423     * @param {string} field
424     * @return {Array.<*>}
425     */
426    value: function(field)
427    {
428        var result = new Array(this.length);
429        for (var i = 0; i < this.length; ++i)
430            result[i] = this[i][field];
431        return result;
432    }
433});
434
435Object.defineProperty(Array.prototype, "peekLast",
436{
437    /**
438     * @this {Array.<*>}
439     * @return {*}
440     */
441    value: function()
442    {
443        return this[this.length - 1];
444    }
445});
446
447/**
448 * @param {*} anObject
449 * @param {Array.<*>} aList
450 * @param {function(*, *)} aFunction
451 */
452function insertionIndexForObjectInListSortedByFunction(anObject, aList, aFunction)
453{
454    var index = binarySearch(anObject, aList, aFunction);
455    if (index < 0)
456        // See binarySearch implementation.
457        return -index - 1;
458    else {
459        // Return the first occurance of an item in the list.
460        while (index > 0 && aFunction(anObject, aList[index - 1]) === 0)
461            index--;
462        return index;
463    }
464}
465
466/**
467 * @param {string} format
468 * @param {...*} var_arg
469 */
470String.sprintf = function(format, var_arg)
471{
472    return String.vsprintf(format, Array.prototype.slice.call(arguments, 1));
473}
474
475String.tokenizeFormatString = function(format, formatters)
476{
477    var tokens = [];
478    var substitutionIndex = 0;
479
480    function addStringToken(str)
481    {
482        tokens.push({ type: "string", value: str });
483    }
484
485    function addSpecifierToken(specifier, precision, substitutionIndex)
486    {
487        tokens.push({ type: "specifier", specifier: specifier, precision: precision, substitutionIndex: substitutionIndex });
488    }
489
490    function isDigit(c)
491    {
492        return !!/[0-9]/.exec(c);
493    }
494
495    var index = 0;
496    for (var precentIndex = format.indexOf("%", index); precentIndex !== -1; precentIndex = format.indexOf("%", index)) {
497        addStringToken(format.substring(index, precentIndex));
498        index = precentIndex + 1;
499
500        if (isDigit(format[index])) {
501            // The first character is a number, it might be a substitution index.
502            var number = parseInt(format.substring(index), 10);
503            while (isDigit(format[index]))
504                ++index;
505
506            // If the number is greater than zero and ends with a "$",
507            // then this is a substitution index.
508            if (number > 0 && format[index] === "$") {
509                substitutionIndex = (number - 1);
510                ++index;
511            }
512        }
513
514        var precision = -1;
515        if (format[index] === ".") {
516            // This is a precision specifier. If no digit follows the ".",
517            // then the precision should be zero.
518            ++index;
519            precision = parseInt(format.substring(index), 10);
520            if (isNaN(precision))
521                precision = 0;
522
523            while (isDigit(format[index]))
524                ++index;
525        }
526
527        if (!(format[index] in formatters)) {
528            addStringToken(format.substring(precentIndex, index + 1));
529            ++index;
530            continue;
531        }
532
533        addSpecifierToken(format[index], precision, substitutionIndex);
534
535        ++substitutionIndex;
536        ++index;
537    }
538
539    addStringToken(format.substring(index));
540
541    return tokens;
542}
543
544String.standardFormatters = {
545    d: function(substitution)
546    {
547        return !isNaN(substitution) ? substitution : 0;
548    },
549
550    f: function(substitution, token)
551    {
552        if (substitution && token.precision > -1)
553            substitution = substitution.toFixed(token.precision);
554        return !isNaN(substitution) ? substitution : (token.precision > -1 ? Number(0).toFixed(token.precision) : 0);
555    },
556
557    s: function(substitution)
558    {
559        return substitution;
560    }
561}
562
563String.vsprintf = function(format, substitutions)
564{
565    return String.format(format, substitutions, String.standardFormatters, "", function(a, b) { return a + b; }).formattedResult;
566}
567
568String.format = function(format, substitutions, formatters, initialValue, append)
569{
570    if (!format || !substitutions || !substitutions.length)
571        return { formattedResult: append(initialValue, format), unusedSubstitutions: substitutions };
572
573    function prettyFunctionName()
574    {
575        return "String.format(\"" + format + "\", \"" + substitutions.join("\", \"") + "\")";
576    }
577
578    function warn(msg)
579    {
580        console.warn(prettyFunctionName() + ": " + msg);
581    }
582
583    function error(msg)
584    {
585        console.error(prettyFunctionName() + ": " + msg);
586    }
587
588    var result = initialValue;
589    var tokens = String.tokenizeFormatString(format, formatters);
590    var usedSubstitutionIndexes = {};
591
592    for (var i = 0; i < tokens.length; ++i) {
593        var token = tokens[i];
594
595        if (token.type === "string") {
596            result = append(result, token.value);
597            continue;
598        }
599
600        if (token.type !== "specifier") {
601            error("Unknown token type \"" + token.type + "\" found.");
602            continue;
603        }
604
605        if (token.substitutionIndex >= substitutions.length) {
606            // If there are not enough substitutions for the current substitutionIndex
607            // just output the format specifier literally and move on.
608            error("not enough substitution arguments. Had " + substitutions.length + " but needed " + (token.substitutionIndex + 1) + ", so substitution was skipped.");
609            result = append(result, "%" + (token.precision > -1 ? token.precision : "") + token.specifier);
610            continue;
611        }
612
613        usedSubstitutionIndexes[token.substitutionIndex] = true;
614
615        if (!(token.specifier in formatters)) {
616            // Encountered an unsupported format character, treat as a string.
617            warn("unsupported format character \u201C" + token.specifier + "\u201D. Treating as a string.");
618            result = append(result, substitutions[token.substitutionIndex]);
619            continue;
620        }
621
622        result = append(result, formatters[token.specifier](substitutions[token.substitutionIndex], token));
623    }
624
625    var unusedSubstitutions = [];
626    for (var i = 0; i < substitutions.length; ++i) {
627        if (i in usedSubstitutionIndexes)
628            continue;
629        unusedSubstitutions.push(substitutions[i]);
630    }
631
632    return { formattedResult: result, unusedSubstitutions: unusedSubstitutions };
633}
634
635/**
636 * @param {string} query
637 * @param {boolean} caseSensitive
638 * @param {boolean} isRegex
639 * @return {RegExp}
640 */
641function createSearchRegex(query, caseSensitive, isRegex)
642{
643    var regexFlags = caseSensitive ? "g" : "gi";
644    var regexObject;
645
646    if (isRegex) {
647        try {
648            regexObject = new RegExp(query, regexFlags);
649        } catch (e) {
650            // Silent catch.
651        }
652    }
653
654    if (!regexObject)
655        regexObject = createPlainTextSearchRegex(query, regexFlags);
656
657    return regexObject;
658}
659
660/**
661 * @param {string} query
662 * @param {string=} flags
663 * @return {!RegExp}
664 */
665function createPlainTextSearchRegex(query, flags)
666{
667    // This should be kept the same as the one in ContentSearchUtils.cpp.
668    var regexSpecialCharacters = String.regexSpecialCharacters();
669    var regex = "";
670    for (var i = 0; i < query.length; ++i) {
671        var c = query.charAt(i);
672        if (regexSpecialCharacters.indexOf(c) != -1)
673            regex += "\\";
674        regex += c;
675    }
676    return new RegExp(regex, flags || "");
677}
678
679/**
680 * @param {RegExp} regex
681 * @param {string} content
682 * @return {number}
683 */
684function countRegexMatches(regex, content)
685{
686    var text = content;
687    var result = 0;
688    var match;
689    while (text && (match = regex.exec(text))) {
690        if (match[0].length > 0)
691            ++result;
692        text = text.substring(match.index + 1);
693    }
694    return result;
695}
696
697/**
698 * @param {number} value
699 * @param {number} symbolsCount
700 * @return {string}
701 */
702function numberToStringWithSpacesPadding(value, symbolsCount)
703{
704    var numberString = value.toString();
705    var paddingLength = Math.max(0, symbolsCount - numberString.length);
706    var paddingString = Array(paddingLength + 1).join("\u00a0");
707    return paddingString + numberString;
708}
709
710/**
711  * @return {string}
712  */
713var createObjectIdentifier = function()
714{
715    // It has to be string for better performance.
716    return '_' + ++createObjectIdentifier._last;
717}
718
719createObjectIdentifier._last = 0;
720
721/**
722 * @constructor
723 */
724var Set = function()
725{
726    /** @type !Object.<string, Object> */
727    this._set = {};
728    this._size = 0;
729}
730
731Set.prototype = {
732    /**
733     * @param {!Object} item
734     */
735    add: function(item)
736    {
737        var objectIdentifier = item.__identifier;
738        if (!objectIdentifier) {
739            objectIdentifier = createObjectIdentifier();
740            item.__identifier = objectIdentifier;
741        }
742        if (!this._set[objectIdentifier])
743            ++this._size;
744        this._set[objectIdentifier] = item;
745    },
746
747    /**
748     * @param {!Object} item
749     */
750    remove: function(item)
751    {
752        if (this._set[item.__identifier]) {
753            --this._size;
754            delete this._set[item.__identifier];
755        }
756    },
757
758    /**
759     * @return {!Array.<Object>}
760     */
761    items: function()
762    {
763        var result = new Array(this._size);
764        var i = 0;
765        for (var objectIdentifier in this._set)
766            result[i++] = this._set[objectIdentifier];
767        return result;
768    },
769
770    /**
771     * @param {!Object} item
772     * @return {?Object}
773     */
774    hasItem: function(item)
775    {
776        return this._set[item.__identifier];
777    },
778
779    /**
780     * @return {number}
781     */
782    size: function()
783    {
784        return this._size;
785    },
786
787    clear: function()
788    {
789        this._set = {};
790        this._size = 0;
791    }
792}
793
794/**
795 * @constructor
796 */
797var Map = function()
798{
799    this._map = {};
800    this._size = 0;
801}
802
803Map.prototype = {
804    /**
805     * @param {Object} key
806     * @param {*=} value
807     */
808    put: function(key, value)
809    {
810        var objectIdentifier = key.__identifier;
811        if (!objectIdentifier) {
812            objectIdentifier = createObjectIdentifier();
813            key.__identifier = objectIdentifier;
814        }
815        if (!this._map[objectIdentifier])
816            ++this._size;
817        this._map[objectIdentifier] = [key, value];
818    },
819
820    /**
821     * @param {Object} key
822     */
823    remove: function(key)
824    {
825        var result = this._map[key.__identifier];
826        if (!result)
827            return undefined;
828        --this._size;
829        delete this._map[key.__identifier];
830        return result[1];
831    },
832
833    /**
834     * @return {Array.<Object>}
835     */
836    keys: function()
837    {
838        return this._list(0);
839    },
840
841    values: function()
842    {
843        return this._list(1);
844    },
845
846    /**
847     * @param {number} index
848     */
849    _list: function(index)
850    {
851        var result = new Array(this._size);
852        var i = 0;
853        for (var objectIdentifier in this._map)
854            result[i++] = this._map[objectIdentifier][index];
855        return result;
856    },
857
858    /**
859     * @param {Object} key
860     */
861    get: function(key)
862    {
863        var entry = this._map[key.__identifier];
864        return entry ? entry[1] : undefined;
865    },
866
867    /**
868     * @param {Object} key
869     */
870    contains: function(key)
871    {
872        var entry = this._map[key.__identifier];
873        return !!entry;
874    },
875
876    size: function()
877    {
878        return this._size;
879    },
880
881    clear: function()
882    {
883        this._map = {};
884        this._size = 0;
885    }
886}
887/**
888 * @param {string} url
889 * @param {boolean=} async
890 * @param {function(?string)=} callback
891 * @return {?string}
892 */
893function loadXHR(url, async, callback)
894{
895    function onReadyStateChanged()
896    {
897        if (xhr.readyState !== XMLHttpRequest.DONE)
898            return;
899
900        if (xhr.status === 200) {
901            callback(xhr.responseText);
902            return;
903        }
904
905        callback(null);
906   }
907
908    var xhr = new XMLHttpRequest();
909    xhr.open("GET", url, async);
910    if (async)
911        xhr.onreadystatechange = onReadyStateChanged;
912    xhr.send(null);
913
914    if (!async) {
915        if (xhr.status === 200)
916            return xhr.responseText;
917        return null;
918    }
919    return null;
920}
921
922/**
923 * @constructor
924 */
925function StringPool()
926{
927    this.reset();
928}
929
930StringPool.prototype = {
931    /**
932     * @param {string} string
933     * @return {string}
934     */
935    intern: function(string)
936    {
937        // Do not mess with setting __proto__ to anything but null, just handle it explicitly.
938        if (string === "__proto__")
939            return "__proto__";
940        var result = this._strings[string];
941        if (result === undefined) {
942            this._strings[string] = string;
943            result = string;
944        }
945        return result;
946    },
947
948    reset: function()
949    {
950        this._strings = Object.create(null);
951    },
952
953    /**
954     * @param {Object} obj
955     * @param {number=} depthLimit
956     */
957    internObjectStrings: function(obj, depthLimit)
958    {
959        if (typeof depthLimit !== "number")
960            depthLimit = 100;
961        else if (--depthLimit < 0)
962            throw "recursion depth limit reached in StringPool.deepIntern(), perhaps attempting to traverse cyclical references?";
963
964        for (var field in obj) {
965            switch (typeof obj[field]) {
966            case "string":
967                obj[field] = this.intern(obj[field]);
968                break;
969            case "object":
970                this.internObjectStrings(obj[field], depthLimit);
971                break;
972            }
973        }
974    }
975}
976
977var _importedScripts = {};
978
979/**
980 * This function behavior depends on the "debug_devtools" flag value.
981 * - In debug mode it loads scripts synchronously via xhr request.
982 * - In release mode every occurrence of "importScript" in the js files
983 *   that have been white listed in the build system gets replaced with
984 *   the script source code on the compilation phase.
985 *   The build system will throw an exception if it found importScript call
986 *   in other files.
987 *
988 * To load scripts lazily in release mode call "loadScript" function.
989 * @param {string} scriptName
990 */
991function importScript(scriptName)
992{
993    if (_importedScripts[scriptName])
994        return;
995    var xhr = new XMLHttpRequest();
996    _importedScripts[scriptName] = true;
997    if (window.flattenImports)
998        scriptName = scriptName.split("/").reverse()[0];
999    xhr.open("GET", scriptName, false);
1000    xhr.send(null);
1001    if (!xhr.responseText)
1002        throw "empty response arrived for script '" + scriptName + "'";
1003    var sourceURL = WebInspector.ParsedURL.completeURL(window.location.href, scriptName);
1004    window.eval(xhr.responseText + "\n//@ sourceURL=" + sourceURL);
1005}
1006
1007var loadScript = importScript;
1008