Home | History | Annotate | Download | only in common
      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 
     30 /**
     31  * @param {!Object} obj
     32  * @return {boolean}
     33  */
     34 Object.isEmpty = function(obj)
     35 {
     36     for (var i in obj)
     37         return false;
     38     return true;
     39 }
     40 
     41 /**
     42  * @param {!Object.<string,!T>} obj
     43  * @return {!Array.<!T>}
     44  * @template T
     45  */
     46 Object.values = function(obj)
     47 {
     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]];
     52     return result;
     53 }
     54 
     55 /**
     56  * @param {number} m
     57  * @param {number} n
     58  * @return {number}
     59  */
     60 function mod(m, n)
     61 {
     62     return ((m % n) + n) % n;
     63 }
     64 
     65 /**
     66  * @param {string} string
     67  * @return {!Array.<number>}
     68  */
     69 String.prototype.findAll = function(string)
     70 {
     71     var matches = [];
     72     var i = this.indexOf(string);
     73     while (i !== -1) {
     74         matches.push(i);
     75         i = this.indexOf(string, i + string.length);
     76     }
     77     return matches;
     78 }
     79 
     80 /**
     81  * @return {!Array.<number>}
     82  */
     83 String.prototype.lineEndings = function()
     84 {
     85     if (!this._lineEndings) {
     86         this._lineEndings = this.findAll("\n");
     87         this._lineEndings.push(this.length);
     88     }
     89     return this._lineEndings;
     90 }
     91 
     92 /**
     93  * @return {number}
     94  */
     95 String.prototype.lineCount = function()
     96 {
     97     var lineEndings = this.lineEndings();
     98     return lineEndings.length;
     99 }
    100 
    101 /**
    102  * @return {string}
    103  */
    104 String.prototype.lineAt = function(lineNumber)
    105 {
    106     var lineEndings = this.lineEndings();
    107     var lineStart = lineNumber > 0 ? lineEndings[lineNumber - 1] + 1 : 0;
    108     var lineEnd = lineEndings[lineNumber];
    109     var lineContent = this.substring(lineStart, lineEnd);
    110     if (lineContent.length > 0 && lineContent.charAt(lineContent.length - 1) === "\r")
    111         lineContent = lineContent.substring(0, lineContent.length - 1);
    112     return lineContent;
    113 }
    114 
    115 /**
    116  * @param {string} chars
    117  * @return {string}
    118  */
    119 String.prototype.escapeCharacters = function(chars)
    120 {
    121     var foundChar = false;
    122     for (var i = 0; i < chars.length; ++i) {
    123         if (this.indexOf(chars.charAt(i)) !== -1) {
    124             foundChar = true;
    125             break;
    126         }
    127     }
    128 
    129     if (!foundChar)
    130         return String(this);
    131 
    132     var result = "";
    133     for (var i = 0; i < this.length; ++i) {
    134         if (chars.indexOf(this.charAt(i)) !== -1)
    135             result += "\\";
    136         result += this.charAt(i);
    137     }
    138 
    139     return result;
    140 }
    141 
    142 /**
    143  * @return {string}
    144  */
    145 String.regexSpecialCharacters = function()
    146 {
    147     return "^[]{}()\\.^$*+?|-,";
    148 }
    149 
    150 /**
    151  * @return {string}
    152  */
    153 String.prototype.escapeForRegExp = function()
    154 {
    155     return this.escapeCharacters(String.regexSpecialCharacters());
    156 }
    157 
    158 /**
    159  * @return {string}
    160  */
    161 String.prototype.escapeHTML = function()
    162 {
    163     return this.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"); //" doublequotes just for editor
    164 }
    165 
    166 /**
    167  * @return {string}
    168  */
    169 String.prototype.collapseWhitespace = function()
    170 {
    171     return this.replace(/[\s\xA0]+/g, " ");
    172 }
    173 
    174 /**
    175  * @param {number} maxLength
    176  * @return {string}
    177  */
    178 String.prototype.trimMiddle = function(maxLength)
    179 {
    180     if (this.length <= maxLength)
    181         return String(this);
    182     var leftHalf = maxLength >> 1;
    183     var rightHalf = maxLength - leftHalf - 1;
    184     return this.substr(0, leftHalf) + "\u2026" + this.substr(this.length - rightHalf, rightHalf);
    185 }
    186 
    187 /**
    188  * @param {number} maxLength
    189  * @return {string}
    190  */
    191 String.prototype.trimEnd = function(maxLength)
    192 {
    193     if (this.length <= maxLength)
    194         return String(this);
    195     return this.substr(0, maxLength - 1) + "\u2026";
    196 }
    197 
    198 /**
    199  * @param {?string=} baseURLDomain
    200  * @return {string}
    201  */
    202 String.prototype.trimURL = function(baseURLDomain)
    203 {
    204     var result = this.replace(/^(https|http|file):\/\//i, "");
    205     if (baseURLDomain)
    206         result = result.replace(new RegExp("^" + baseURLDomain.escapeForRegExp(), "i"), "");
    207     return result;
    208 }
    209 
    210 /**
    211  * @return {string}
    212  */
    213 String.prototype.toTitleCase = function()
    214 {
    215     return this.substring(0, 1).toUpperCase() + this.substring(1);
    216 }
    217 
    218 /**
    219  * @param {string} other
    220  * @return {number}
    221  */
    222 String.prototype.compareTo = function(other)
    223 {
    224     if (this > other)
    225         return 1;
    226     if (this < other)
    227         return -1;
    228     return 0;
    229 }
    230 
    231 /**
    232  * @param {string} href
    233  * @return {?string}
    234  */
    235 function sanitizeHref(href)
    236 {
    237     return href && href.trim().toLowerCase().startsWith("javascript:") ? null : href;
    238 }
    239 
    240 /**
    241  * @return {string}
    242  */
    243 String.prototype.removeURLFragment = function()
    244 {
    245     var fragmentIndex = this.indexOf("#");
    246     if (fragmentIndex == -1)
    247         fragmentIndex = this.length;
    248     return this.substring(0, fragmentIndex);
    249 }
    250 
    251 /**
    252  * @return {boolean}
    253  */
    254 String.prototype.startsWith = function(substring)
    255 {
    256     return !this.lastIndexOf(substring, 0);
    257 }
    258 
    259 /**
    260  * @return {boolean}
    261  */
    262 String.prototype.endsWith = function(substring)
    263 {
    264     return this.indexOf(substring, this.length - substring.length) !== -1;
    265 }
    266 
    267 /**
    268  * @return {number}
    269  */
    270 String.prototype.hashCode = function()
    271 {
    272     var result = 0;
    273     for (var i = 0; i < this.length; ++i)
    274         result = result * 3 + this.charCodeAt(i);
    275     return result;
    276 }
    277 
    278 /**
    279  * @param {string} a
    280  * @param {string} b
    281  * @return {number}
    282  */
    283 String.naturalOrderComparator = function(a, b)
    284 {
    285     var chunk = /^\d+|^\D+/;
    286     var chunka, chunkb, anum, bnum;
    287     while (1) {
    288         if (a) {
    289             if (!b)
    290                 return 1;
    291         } else {
    292             if (b)
    293                 return -1;
    294             else
    295                 return 0;
    296         }
    297         chunka = a.match(chunk)[0];
    298         chunkb = b.match(chunk)[0];
    299         anum = !isNaN(chunka);
    300         bnum = !isNaN(chunkb);
    301         if (anum && !bnum)
    302             return -1;
    303         if (bnum && !anum)
    304             return 1;
    305         if (anum && bnum) {
    306             var diff = chunka - chunkb;
    307             if (diff)
    308                 return diff;
    309             if (chunka.length !== chunkb.length) {
    310                 if (!+chunka && !+chunkb) // chunks are strings of all 0s (special case)
    311                     return chunka.length - chunkb.length;
    312                 else
    313                     return chunkb.length - chunka.length;
    314             }
    315         } else if (chunka !== chunkb)
    316             return (chunka < chunkb) ? -1 : 1;
    317         a = a.substring(chunka.length);
    318         b = b.substring(chunkb.length);
    319     }
    320 }
    321 
    322 /**
    323  * @param {number} num
    324  * @param {number} min
    325  * @param {number} max
    326  * @return {number}
    327  */
    328 Number.constrain = function(num, min, max)
    329 {
    330     if (num < min)
    331         num = min;
    332     else if (num > max)
    333         num = max;
    334     return num;
    335 }
    336 
    337 /**
    338  * @param {number} a
    339  * @param {number} b
    340  * @return {number}
    341  */
    342 Number.gcd = function(a, b)
    343 {
    344     if (b === 0)
    345         return a;
    346     else
    347         return Number.gcd(b, a % b);
    348 }
    349 
    350 /**
    351  * @param {string} value
    352  * @return {string}
    353  */
    354 Number.toFixedIfFloating = function(value)
    355 {
    356     if (!value || isNaN(value))
    357         return value;
    358     var number = Number(value);
    359     return number % 1 ? number.toFixed(3) : String(number);
    360 }
    361 
    362 /**
    363  * @return {string}
    364  */
    365 Date.prototype.toISO8601Compact = function()
    366 {
    367     /**
    368      * @param {number} x
    369      * @return {string}
    370      */
    371     function leadZero(x)
    372     {
    373         return (x > 9 ? "" : "0") + x;
    374     }
    375     return this.getFullYear() +
    376            leadZero(this.getMonth() + 1) +
    377            leadZero(this.getDate()) + "T" +
    378            leadZero(this.getHours()) +
    379            leadZero(this.getMinutes()) +
    380            leadZero(this.getSeconds());
    381 }
    382 
    383 /**
    384  * @return {string}
    385  */
    386  Date.prototype.toConsoleTime = function()
    387 {
    388     /**
    389      * @param {number} x
    390      * @return {string}
    391      */
    392     function leadZero2(x)
    393     {
    394         return (x > 9 ? "" : "0") + x;
    395     }
    396 
    397     /**
    398      * @param {number} x
    399      * @return {string}
    400      */
    401     function leadZero3(x)
    402     {
    403         return (Array(4 - x.toString().length)).join('0') + x;
    404     }
    405 
    406     return this.getFullYear() + "-" +
    407            leadZero2(this.getMonth() + 1) + "-" +
    408            leadZero2(this.getDate()) + " " +
    409            leadZero2(this.getHours()) + ":" +
    410            leadZero2(this.getMinutes()) + ":" +
    411            leadZero2(this.getSeconds()) + "." +
    412            leadZero3(this.getMilliseconds());
    413 }
    414 
    415 Object.defineProperty(Array.prototype, "remove",
    416 {
    417     /**
    418      * @param {!T} value
    419      * @param {boolean=} firstOnly
    420      * @this {Array.<!T>}
    421      * @template T
    422      */
    423     value: function(value, firstOnly)
    424     {
    425         var index = this.indexOf(value);
    426         if (index === -1)
    427             return;
    428         if (firstOnly) {
    429             this.splice(index, 1);
    430             return;
    431         }
    432         for (var i = index + 1, n = this.length; i < n; ++i) {
    433             if (this[i] !== value)
    434                 this[index++] = this[i];
    435         }
    436         this.length = index;
    437     }
    438 });
    439 
    440 Object.defineProperty(Array.prototype, "keySet",
    441 {
    442     /**
    443      * @return {!Object.<string, boolean>}
    444      * @this {Array.<*>}
    445      */
    446     value: function()
    447     {
    448         var keys = {};
    449         for (var i = 0; i < this.length; ++i)
    450             keys[this[i]] = true;
    451         return keys;
    452     }
    453 });
    454 
    455 Object.defineProperty(Array.prototype, "pushAll",
    456 {
    457     /**
    458      * @param {!Array.<!T>} array
    459      * @this {Array.<!T>}
    460      * @template T
    461      */
    462     value: function(array)
    463     {
    464         Array.prototype.push.apply(this, array);
    465     }
    466 });
    467 
    468 Object.defineProperty(Array.prototype, "rotate",
    469 {
    470     /**
    471      * @param {number} index
    472      * @return {!Array.<!T>}
    473      * @this {Array.<!T>}
    474      * @template T
    475      */
    476     value: function(index)
    477     {
    478         var result = [];
    479         for (var i = index; i < index + this.length; ++i)
    480             result.push(this[i % this.length]);
    481         return result;
    482     }
    483 });
    484 
    485 Object.defineProperty(Array.prototype, "sortNumbers",
    486 {
    487     /**
    488      * @this {Array.<number>}
    489      */
    490     value: function()
    491     {
    492         /**
    493          * @param {number} a
    494          * @param {number} b
    495          * @return {number}
    496          */
    497         function numericComparator(a, b)
    498         {
    499             return a - b;
    500         }
    501 
    502         this.sort(numericComparator);
    503     }
    504 });
    505 
    506 Object.defineProperty(Uint32Array.prototype, "sort", {
    507     value: Array.prototype.sort
    508 });
    509 
    510 (function() {
    511 var partition = {
    512     /**
    513      * @this {Array.<number>}
    514      * @param {function(number, number): number} comparator
    515      * @param {number} left
    516      * @param {number} right
    517      * @param {number} pivotIndex
    518      */
    519     value: function(comparator, left, right, pivotIndex)
    520     {
    521         function swap(array, i1, i2)
    522         {
    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, sortWindowRight)
    555     {
    556         function quickSortRange(array, comparator, left, right, sortWindowLeft, sortWindowRight)
    557         {
    558             if (right <= left)
    559                 return;
    560             var pivotIndex = Math.floor(Math.random() * (right - left)) + left;
    561             var pivotNewIndex = array.partition(comparator, left, right, pivotIndex);
    562             if (sortWindowLeft < pivotNewIndex)
    563                 quickSortRange(array, comparator, left, pivotNewIndex - 1, sortWindowLeft, sortWindowRight);
    564             if (pivotNewIndex < sortWindowRight)
    565                 quickSortRange(array, comparator, pivotNewIndex + 1, right, sortWindowLeft, sortWindowRight);
    566         }
    567         if (leftBound === 0 && rightBound === (this.length - 1) && sortWindowLeft === 0 && sortWindowRight >= rightBound)
    568             this.sort(comparator);
    569         else
    570             quickSortRange(this, comparator, leftBound, rightBound, sortWindowLeft, sortWindowRight);
    571         return this;
    572     }
    573 }
    574 Object.defineProperty(Array.prototype, "sortRange", sortRange);
    575 Object.defineProperty(Uint32Array.prototype, "sortRange", sortRange);
    576 })();
    577 
    578 Object.defineProperty(Array.prototype, "stableSort",
    579 {
    580     /**
    581      * @param {function(?T, ?T): number=} comparator
    582      * @return {!Array.<?T>}
    583      * @this {Array.<?T>}
    584      * @template T
    585      */
    586     value: function(comparator)
    587     {
    588         function defaultComparator(a, b)
    589         {
    590             return a < b ? -1 : (a > b ? 1 : 0);
    591         }
    592         comparator = comparator || defaultComparator;
    593 
    594         var indices = new Array(this.length);
    595         for (var i = 0; i < this.length; ++i)
    596             indices[i] = i;
    597         var self = this;
    598         /**
    599          * @param {number} a
    600          * @param {number} b
    601          * @return {number}
    602          */
    603         function indexComparator(a, b)
    604         {
    605             var result = comparator(self[a], self[b]);
    606             return result ? result : a - b;
    607         }
    608         indices.sort(indexComparator);
    609 
    610         for (var i = 0; i < this.length; ++i) {
    611             if (indices[i] < 0 || i === indices[i])
    612                 continue;
    613             var cyclical = i;
    614             var saved = this[i];
    615             while (true) {
    616                 var next = indices[cyclical];
    617                 indices[cyclical] = -1;
    618                 if (next === i) {
    619                     this[cyclical] = saved;
    620                     break;
    621                 } else {
    622                     this[cyclical] = this[next];
    623                     cyclical = next;
    624                 }
    625             }
    626         }
    627         return this;
    628     }
    629 });
    630 
    631 Object.defineProperty(Array.prototype, "qselect",
    632 {
    633     /**
    634      * @param {number} k
    635      * @param {function(number, number): number=} comparator
    636      * @return {number|undefined}
    637      * @this {Array.<number>}
    638      */
    639     value: function(k, comparator)
    640     {
    641         if (k < 0 || k >= this.length)
    642             return;
    643         if (!comparator)
    644             comparator = function(a, b) { return a - b; }
    645 
    646         var low = 0;
    647         var high = this.length - 1;
    648         for (;;) {
    649             var pivotPosition = this.partition(comparator, low, high, Math.floor((high + low) / 2));
    650             if (pivotPosition === k)
    651                 return this[k];
    652             else if (pivotPosition > k)
    653                 high = pivotPosition - 1;
    654             else
    655                 low = pivotPosition + 1;
    656         }
    657     }
    658 });
    659 
    660 Object.defineProperty(Array.prototype, "lowerBound",
    661 {
    662     /**
    663      * Return index of the leftmost element that is equal or greater
    664      * than the specimen object. If there's no such element (i.e. all
    665      * elements are smaller than the specimen) returns right bound.
    666      * The function works for sorted array.
    667      * When specified, |left| (inclusive) and |right| (exclusive) indices
    668      * define the search window.
    669      *
    670      * @param {!T} object
    671      * @param {function(!T,!S):number=} comparator
    672      * @param {number=} left
    673      * @param {number=} right
    674      * @return {number}
    675      * @this {Array.<!S>}
    676      * @template T,S
    677      */
    678     value: function(object, comparator, left, right)
    679     {
    680         function defaultComparator(a, b)
    681         {
    682             return a < b ? -1 : (a > b ? 1 : 0);
    683         }
    684         comparator = comparator || defaultComparator;
    685         var l = left || 0;
    686         var r = right !== undefined ? right : this.length;
    687         while (l < r) {
    688             var m = (l + r) >> 1;
    689             if (comparator(object, this[m]) > 0)
    690                 l = m + 1;
    691             else
    692                 r = m;
    693         }
    694         return r;
    695     }
    696 });
    697 
    698 Object.defineProperty(Array.prototype, "upperBound",
    699 {
    700     /**
    701      * Return index of the leftmost element that is greater
    702      * than the specimen object. If there's no such element (i.e. all
    703      * elements are smaller or equal to the specimen) returns right bound.
    704      * The function works for sorted array.
    705      * When specified, |left| (inclusive) and |right| (exclusive) indices
    706      * define the search window.
    707      *
    708      * @param {!T} object
    709      * @param {function(!T,!S):number=} comparator
    710      * @param {number=} left
    711      * @param {number=} right
    712      * @return {number}
    713      * @this {Array.<!S>}
    714      * @template T,S
    715      */
    716     value: function(object, comparator, left, right)
    717     {
    718         function defaultComparator(a, b)
    719         {
    720             return a < b ? -1 : (a > b ? 1 : 0);
    721         }
    722         comparator = comparator || defaultComparator;
    723         var l = left || 0;
    724         var r = right !== undefined ? right : this.length;
    725         while (l < r) {
    726             var m = (l + r) >> 1;
    727             if (comparator(object, this[m]) >= 0)
    728                 l = m + 1;
    729             else
    730                 r = m;
    731         }
    732         return r;
    733     }
    734 });
    735 
    736 Object.defineProperty(Uint32Array.prototype, "lowerBound", {
    737     value: Array.prototype.lowerBound
    738 });
    739 
    740 Object.defineProperty(Uint32Array.prototype, "upperBound", {
    741     value: Array.prototype.upperBound
    742 });
    743 
    744 Object.defineProperty(Float64Array.prototype, "lowerBound", {
    745     value: Array.prototype.lowerBound
    746 });
    747 
    748 Object.defineProperty(Array.prototype, "binaryIndexOf",
    749 {
    750     /**
    751      * @param {!T} value
    752      * @param {function(!T,!S):number} comparator
    753      * @return {number}
    754      * @this {Array.<!S>}
    755      * @template T,S
    756      */
    757     value: function(value, comparator)
    758     {
    759         var index = this.lowerBound(value, comparator);
    760         return index < this.length && comparator(value, this[index]) === 0 ? index : -1;
    761     }
    762 });
    763 
    764 Object.defineProperty(Array.prototype, "select",
    765 {
    766     /**
    767      * @param {string} field
    768      * @return {!Array.<!T>}
    769      * @this {Array.<!Object.<string,!T>>}
    770      * @template T
    771      */
    772     value: function(field)
    773     {
    774         var result = new Array(this.length);
    775         for (var i = 0; i < this.length; ++i)
    776             result[i] = this[i][field];
    777         return result;
    778     }
    779 });
    780 
    781 Object.defineProperty(Array.prototype, "peekLast",
    782 {
    783     /**
    784      * @return {!T|undefined}
    785      * @this {Array.<!T>}
    786      * @template T
    787      */
    788     value: function()
    789     {
    790         return this[this.length - 1];
    791     }
    792 });
    793 
    794 (function(){
    795 
    796 /**
    797  * @param {!Array.<T>} array1
    798  * @param {!Array.<T>} array2
    799  * @param {function(T,T):number} comparator
    800  * @param {boolean} mergeNotIntersect
    801  * @return {!Array.<T>}
    802  * @template T
    803  */
    804 function mergeOrIntersect(array1, array2, comparator, mergeNotIntersect)
    805 {
    806     var result = [];
    807     var i = 0;
    808     var j = 0;
    809     while (i < array1.length && j < array2.length) {
    810         var compareValue = comparator(array1[i], array2[j]);
    811         if (mergeNotIntersect || !compareValue)
    812             result.push(compareValue <= 0 ? array1[i] : array2[j]);
    813         if (compareValue <= 0)
    814             i++;
    815         if (compareValue >= 0)
    816             j++;
    817     }
    818     if (mergeNotIntersect) {
    819         while (i < array1.length)
    820             result.push(array1[i++]);
    821         while (j < array2.length)
    822             result.push(array2[j++]);
    823     }
    824     return result;
    825 }
    826 
    827 Object.defineProperty(Array.prototype, "intersectOrdered",
    828 {
    829     /**
    830      * @param {!Array.<T>} array
    831      * @param {function(T,T):number} comparator
    832      * @return {!Array.<T>}
    833      * @this {!Array.<T>}
    834      * @template T
    835      */
    836     value: function(array, comparator)
    837     {
    838         return mergeOrIntersect(this, array, comparator, false);
    839     }
    840 });
    841 
    842 Object.defineProperty(Array.prototype, "mergeOrdered",
    843 {
    844     /**
    845      * @param {!Array.<T>} array
    846      * @param {function(T,T):number} comparator
    847      * @return {!Array.<T>}
    848      * @this {!Array.<T>}
    849      * @template T
    850      */
    851     value: function(array, comparator)
    852     {
    853         return mergeOrIntersect(this, array, comparator, true);
    854     }
    855 });
    856 
    857 }());
    858 
    859 
    860 /**
    861  * @param {!T} object
    862  * @param {!Array.<!S>} list
    863  * @param {function(!T,!S):number=} comparator
    864  * @param {boolean=} insertionIndexAfter
    865  * @return {number}
    866  * @template T,S
    867  */
    868 function insertionIndexForObjectInListSortedByFunction(object, list, comparator, insertionIndexAfter)
    869 {
    870     if (insertionIndexAfter)
    871         return list.upperBound(object, comparator);
    872     else
    873         return list.lowerBound(object, comparator);
    874 }
    875 
    876 /**
    877  * @param {string} format
    878  * @param {...*} var_arg
    879  * @return {string}
    880  */
    881 String.sprintf = function(format, var_arg)
    882 {
    883     return String.vsprintf(format, Array.prototype.slice.call(arguments, 1));
    884 }
    885 
    886 /**
    887  * @param {string} format
    888  * @param {!Object.<string, function(string, ...):*>} formatters
    889  * @return {!Array.<!Object>}
    890  */
    891 String.tokenizeFormatString = function(format, formatters)
    892 {
    893     var tokens = [];
    894     var substitutionIndex = 0;
    895 
    896     function addStringToken(str)
    897     {
    898         tokens.push({ type: "string", value: str });
    899     }
    900 
    901     function addSpecifierToken(specifier, precision, substitutionIndex)
    902     {
    903         tokens.push({ type: "specifier", specifier: specifier, precision: precision, substitutionIndex: substitutionIndex });
    904     }
    905 
    906     function isDigit(c)
    907     {
    908         return !!/[0-9]/.exec(c);
    909     }
    910 
    911     var index = 0;
    912     for (var precentIndex = format.indexOf("%", index); precentIndex !== -1; precentIndex = format.indexOf("%", index)) {
    913         addStringToken(format.substring(index, precentIndex));
    914         index = precentIndex + 1;
    915 
    916         if (format[index] === "%") {
    917             // %% escape sequence.
    918             addStringToken("%");
    919             ++index;
    920             continue;
    921         }
    922 
    923         if (isDigit(format[index])) {
    924             // The first character is a number, it might be a substitution index.
    925             var number = parseInt(format.substring(index), 10);
    926             while (isDigit(format[index]))
    927                 ++index;
    928 
    929             // If the number is greater than zero and ends with a "$",
    930             // then this is a substitution index.
    931             if (number > 0 && format[index] === "$") {
    932                 substitutionIndex = (number - 1);
    933                 ++index;
    934             }
    935         }
    936 
    937         var precision = -1;
    938         if (format[index] === ".") {
    939             // This is a precision specifier. If no digit follows the ".",
    940             // then the precision should be zero.
    941             ++index;
    942             precision = parseInt(format.substring(index), 10);
    943             if (isNaN(precision))
    944                 precision = 0;
    945 
    946             while (isDigit(format[index]))
    947                 ++index;
    948         }
    949 
    950         if (!(format[index] in formatters)) {
    951             addStringToken(format.substring(precentIndex, index + 1));
    952             ++index;
    953             continue;
    954         }
    955 
    956         addSpecifierToken(format[index], precision, substitutionIndex);
    957 
    958         ++substitutionIndex;
    959         ++index;
    960     }
    961 
    962     addStringToken(format.substring(index));
    963 
    964     return tokens;
    965 }
    966 
    967 String.standardFormatters = {
    968     /**
    969      * @return {number}
    970      */
    971     d: function(substitution)
    972     {
    973         return !isNaN(substitution) ? substitution : 0;
    974     },
    975 
    976     /**
    977      * @return {number}
    978      */
    979     f: function(substitution, token)
    980     {
    981         if (substitution && token.precision > -1)
    982             substitution = substitution.toFixed(token.precision);
    983         return !isNaN(substitution) ? substitution : (token.precision > -1 ? Number(0).toFixed(token.precision) : 0);
    984     },
    985 
    986     /**
    987      * @return {string}
    988      */
    989     s: function(substitution)
    990     {
    991         return substitution;
    992     }
    993 }
    994 
    995 /**
    996  * @param {string} format
    997  * @param {!Array.<*>} substitutions
    998  * @return {string}
    999  */
   1000 String.vsprintf = function(format, substitutions)
   1001 {
   1002     return String.format(format, substitutions, String.standardFormatters, "", function(a, b) { return a + b; }).formattedResult;
   1003 }
   1004 
   1005 /**
   1006  * @param {string} format
   1007  * @param {?Array.<string>} substitutions
   1008  * @param {!Object.<string, function(string, ...):string>} formatters
   1009  * @param {!T} initialValue
   1010  * @param {function(T, string): T|undefined} append
   1011  * @return {!{formattedResult: T, unusedSubstitutions: ?Array.<string>}};
   1012  * @template T
   1013  */
   1014 String.format = function(format, substitutions, formatters, initialValue, append)
   1015 {
   1016     if (!format || !substitutions || !substitutions.length)
   1017         return { formattedResult: append(initialValue, format), unusedSubstitutions: substitutions };
   1018 
   1019     function prettyFunctionName()
   1020     {
   1021         return "String.format(\"" + format + "\", \"" + substitutions.join("\", \"") + "\")";
   1022     }
   1023 
   1024     function warn(msg)
   1025     {
   1026         console.warn(prettyFunctionName() + ": " + msg);
   1027     }
   1028 
   1029     function error(msg)
   1030     {
   1031         console.error(prettyFunctionName() + ": " + msg);
   1032     }
   1033 
   1034     var result = initialValue;
   1035     var tokens = String.tokenizeFormatString(format, formatters);
   1036     var usedSubstitutionIndexes = {};
   1037 
   1038     for (var i = 0; i < tokens.length; ++i) {
   1039         var token = tokens[i];
   1040 
   1041         if (token.type === "string") {
   1042             result = append(result, token.value);
   1043             continue;
   1044         }
   1045 
   1046         if (token.type !== "specifier") {
   1047             error("Unknown token type \"" + token.type + "\" found.");
   1048             continue;
   1049         }
   1050 
   1051         if (token.substitutionIndex >= substitutions.length) {
   1052             // If there are not enough substitutions for the current substitutionIndex
   1053             // just output the format specifier literally and move on.
   1054             error("not enough substitution arguments. Had " + substitutions.length + " but needed " + (token.substitutionIndex + 1) + ", so substitution was skipped.");
   1055             result = append(result, "%" + (token.precision > -1 ? token.precision : "") + token.specifier);
   1056             continue;
   1057         }
   1058 
   1059         usedSubstitutionIndexes[token.substitutionIndex] = true;
   1060 
   1061         if (!(token.specifier in formatters)) {
   1062             // Encountered an unsupported format character, treat as a string.
   1063             warn("unsupported format character \u201C" + token.specifier + "\u201D. Treating as a string.");
   1064             result = append(result, substitutions[token.substitutionIndex]);
   1065             continue;
   1066         }
   1067 
   1068         result = append(result, formatters[token.specifier](substitutions[token.substitutionIndex], token));
   1069     }
   1070 
   1071     var unusedSubstitutions = [];
   1072     for (var i = 0; i < substitutions.length; ++i) {
   1073         if (i in usedSubstitutionIndexes)
   1074             continue;
   1075         unusedSubstitutions.push(substitutions[i]);
   1076     }
   1077 
   1078     return { formattedResult: result, unusedSubstitutions: unusedSubstitutions };
   1079 }
   1080 
   1081 /**
   1082  * @param {string} query
   1083  * @param {boolean} caseSensitive
   1084  * @param {boolean} isRegex
   1085  * @return {!RegExp}
   1086  */
   1087 function createSearchRegex(query, caseSensitive, isRegex)
   1088 {
   1089     var regexFlags = caseSensitive ? "g" : "gi";
   1090     var regexObject;
   1091 
   1092     if (isRegex) {
   1093         try {
   1094             regexObject = new RegExp(query, regexFlags);
   1095         } catch (e) {
   1096             // Silent catch.
   1097         }
   1098     }
   1099 
   1100     if (!regexObject)
   1101         regexObject = createPlainTextSearchRegex(query, regexFlags);
   1102 
   1103     return regexObject;
   1104 }
   1105 
   1106 /**
   1107  * @param {string} query
   1108  * @param {string=} flags
   1109  * @return {!RegExp}
   1110  */
   1111 function createPlainTextSearchRegex(query, flags)
   1112 {
   1113     // This should be kept the same as the one in ContentSearchUtils.cpp.
   1114     var regexSpecialCharacters = String.regexSpecialCharacters();
   1115     var regex = "";
   1116     for (var i = 0; i < query.length; ++i) {
   1117         var c = query.charAt(i);
   1118         if (regexSpecialCharacters.indexOf(c) != -1)
   1119             regex += "\\";
   1120         regex += c;
   1121     }
   1122     return new RegExp(regex, flags || "");
   1123 }
   1124 
   1125 /**
   1126  * @param {!RegExp} regex
   1127  * @param {string} content
   1128  * @return {number}
   1129  */
   1130 function countRegexMatches(regex, content)
   1131 {
   1132     var text = content;
   1133     var result = 0;
   1134     var match;
   1135     while (text && (match = regex.exec(text))) {
   1136         if (match[0].length > 0)
   1137             ++result;
   1138         text = text.substring(match.index + 1);
   1139     }
   1140     return result;
   1141 }
   1142 
   1143 /**
   1144  * @param {number} value
   1145  * @param {number} symbolsCount
   1146  * @return {string}
   1147  */
   1148 function numberToStringWithSpacesPadding(value, symbolsCount)
   1149 {
   1150     var numberString = value.toString();
   1151     var paddingLength = Math.max(0, symbolsCount - numberString.length);
   1152     var paddingString = Array(paddingLength + 1).join("\u00a0");
   1153     return paddingString + numberString;
   1154 }
   1155 
   1156 /**
   1157  * @return {string}
   1158  */
   1159 var createObjectIdentifier = function()
   1160 {
   1161     // It has to be string for better performance.
   1162     return "_" + ++createObjectIdentifier._last;
   1163 }
   1164 
   1165 createObjectIdentifier._last = 0;
   1166 
   1167 /**
   1168  * @constructor
   1169  * @template T
   1170  */
   1171 var Set = function()
   1172 {
   1173     /** @type {!Object.<string, !T>} */
   1174     this._set = {};
   1175     this._size = 0;
   1176 }
   1177 
   1178 /**
   1179  * @param {!Array.<!T>} array
   1180  * @return {!Set.<T>}
   1181  * @template T
   1182  */
   1183 Set.fromArray = function(array)
   1184 {
   1185     var result = new Set();
   1186     array.forEach(function(item) { result.add(item); });
   1187     return result;
   1188 }
   1189 
   1190 Set.prototype = {
   1191     /**
   1192      * @param {!T} item
   1193      */
   1194     add: function(item)
   1195     {
   1196         var objectIdentifier = item.__identifier;
   1197         if (!objectIdentifier) {
   1198             objectIdentifier = createObjectIdentifier();
   1199             item.__identifier = objectIdentifier;
   1200         }
   1201         if (!this._set[objectIdentifier])
   1202             ++this._size;
   1203         this._set[objectIdentifier] = item;
   1204     },
   1205 
   1206     /**
   1207      * @param {!T} item
   1208      * @return {boolean}
   1209      */
   1210     remove: function(item)
   1211     {
   1212         if (this._set[item.__identifier]) {
   1213             --this._size;
   1214             delete this._set[item.__identifier];
   1215             return true;
   1216         }
   1217         return false;
   1218     },
   1219 
   1220     /**
   1221      * @return {!Array.<!T>}
   1222      */
   1223     values: function()
   1224     {
   1225         var result = new Array(this._size);
   1226         var i = 0;
   1227         for (var objectIdentifier in this._set)
   1228             result[i++] = this._set[objectIdentifier];
   1229         return result;
   1230     },
   1231 
   1232     /**
   1233      * @param {!T} item
   1234      * @return {boolean}
   1235      */
   1236     contains: function(item)
   1237     {
   1238         return !!this._set[item.__identifier];
   1239     },
   1240 
   1241     /**
   1242      * @return {number}
   1243      */
   1244     size: function()
   1245     {
   1246         return this._size;
   1247     },
   1248 
   1249     clear: function()
   1250     {
   1251         this._set = {};
   1252         this._size = 0;
   1253     }
   1254 }
   1255 
   1256 /**
   1257  * @constructor
   1258  * @template K,V
   1259  */
   1260 var Map = function()
   1261 {
   1262     /** @type {!Object.<string, !Array.<K|V>>} */
   1263     this._map = {};
   1264     this._size = 0;
   1265 }
   1266 
   1267 Map.prototype = {
   1268     /**
   1269      * @param {K} key
   1270      * @param {V} value
   1271      */
   1272     put: function(key, value)
   1273     {
   1274         var objectIdentifier = key.__identifier;
   1275         if (!objectIdentifier) {
   1276             objectIdentifier = createObjectIdentifier();
   1277             key.__identifier = objectIdentifier;
   1278         }
   1279         if (!this._map[objectIdentifier])
   1280             ++this._size;
   1281         this._map[objectIdentifier] = [key, value];
   1282     },
   1283 
   1284     /**
   1285      * @param {K} key
   1286      * @return {V}
   1287      */
   1288     remove: function(key)
   1289     {
   1290         var result = this._map[key.__identifier];
   1291         if (!result)
   1292             return undefined;
   1293         --this._size;
   1294         delete this._map[key.__identifier];
   1295         return result[1];
   1296     },
   1297 
   1298     /**
   1299      * @return {!Array.<K>}
   1300      */
   1301     keys: function()
   1302     {
   1303         return this._list(0);
   1304     },
   1305 
   1306     /**
   1307      * @return {!Array.<V>}
   1308      */
   1309     values: function()
   1310     {
   1311         return this._list(1);
   1312     },
   1313 
   1314     /**
   1315      * @param {number} index
   1316      * @return {!Array.<K|V>}
   1317      */
   1318     _list: function(index)
   1319     {
   1320         var result = new Array(this._size);
   1321         var i = 0;
   1322         for (var objectIdentifier in this._map)
   1323             result[i++] = this._map[objectIdentifier][index];
   1324         return result;
   1325     },
   1326 
   1327     /**
   1328      * @param {K} key
   1329      * @return {V|undefined}
   1330      */
   1331     get: function(key)
   1332     {
   1333         var entry = this._map[key.__identifier];
   1334         return entry ? entry[1] : undefined;
   1335     },
   1336 
   1337     /**
   1338      * @param {K} key
   1339      * @return {boolean}
   1340      */
   1341     contains: function(key)
   1342     {
   1343         var entry = this._map[key.__identifier];
   1344         return !!entry;
   1345     },
   1346 
   1347     /**
   1348      * @return {number}
   1349      */
   1350     size: function()
   1351     {
   1352         return this._size;
   1353     },
   1354 
   1355     clear: function()
   1356     {
   1357         this._map = {};
   1358         this._size = 0;
   1359     }
   1360 }
   1361 
   1362 /**
   1363  * @constructor
   1364  * @template T
   1365  */
   1366 var StringMap = function()
   1367 {
   1368     /** @type {!Object.<string, T>} */
   1369     this._map = {};
   1370     this._size = 0;
   1371 }
   1372 
   1373 StringMap.prototype = {
   1374     /**
   1375      * @param {string} key
   1376      * @param {T} value
   1377      */
   1378     put: function(key, value)
   1379     {
   1380         if (key === "__proto__") {
   1381             if (!this._hasProtoKey) {
   1382                 ++this._size;
   1383                 this._hasProtoKey = true;
   1384             }
   1385             /** @type {T} */
   1386             this._protoValue = value;
   1387             return;
   1388         }
   1389         if (!Object.prototype.hasOwnProperty.call(this._map, key))
   1390             ++this._size;
   1391         this._map[key] = value;
   1392     },
   1393 
   1394     /**
   1395      * @param {string} key
   1396      * @return {T|undefined}
   1397      */
   1398     remove: function(key)
   1399     {
   1400         var result;
   1401         if (key === "__proto__") {
   1402             if (!this._hasProtoKey)
   1403                 return undefined;
   1404             --this._size;
   1405             delete this._hasProtoKey;
   1406             result = this._protoValue;
   1407             delete this._protoValue;
   1408             return result;
   1409         }
   1410         if (!Object.prototype.hasOwnProperty.call(this._map, key))
   1411             return undefined;
   1412         --this._size;
   1413         result = this._map[key];
   1414         delete this._map[key];
   1415         return result;
   1416     },
   1417 
   1418     /**
   1419      * @return {!Array.<string>}
   1420      */
   1421     keys: function()
   1422     {
   1423         var result = Object.keys(this._map) || [];
   1424         if (this._hasProtoKey)
   1425             result.push("__proto__");
   1426         return result;
   1427     },
   1428 
   1429     /**
   1430      * @return {!Array.<T>}
   1431      */
   1432     values: function()
   1433     {
   1434         var result = Object.values(this._map);
   1435         if (this._hasProtoKey)
   1436             result.push(this._protoValue);
   1437         return result;
   1438     },
   1439 
   1440     /**
   1441      * @param {string} key
   1442      * @return {T|undefined}
   1443      */
   1444     get: function(key)
   1445     {
   1446         if (key === "__proto__")
   1447             return this._protoValue;
   1448         if (!Object.prototype.hasOwnProperty.call(this._map, key))
   1449             return undefined;
   1450         return this._map[key];
   1451     },
   1452 
   1453     /**
   1454      * @param {string} key
   1455      * @return {boolean}
   1456      */
   1457     contains: function(key)
   1458     {
   1459         var result;
   1460         if (key === "__proto__")
   1461             return this._hasProtoKey;
   1462         return Object.prototype.hasOwnProperty.call(this._map, key);
   1463     },
   1464 
   1465     /**
   1466      * @return {number}
   1467      */
   1468     size: function()
   1469     {
   1470         return this._size;
   1471     },
   1472 
   1473     clear: function()
   1474     {
   1475         this._map = {};
   1476         this._size = 0;
   1477         delete this._hasProtoKey;
   1478         delete this._protoValue;
   1479     }
   1480 }
   1481 
   1482 /**
   1483  * @constructor
   1484  * @extends {StringMap.<Set.<!T>>}
   1485  * @template T
   1486  */
   1487 var StringMultimap = function()
   1488 {
   1489     StringMap.call(this);
   1490 }
   1491 
   1492 StringMultimap.prototype = {
   1493     /**
   1494      * @param {string} key
   1495      * @param {T} value
   1496      */
   1497     put: function(key, value)
   1498     {
   1499         if (key === "__proto__") {
   1500             if (!this._hasProtoKey) {
   1501                 ++this._size;
   1502                 this._hasProtoKey = true;
   1503                 /** @type {!Set.<T>} */
   1504                 this._protoValue = new Set();
   1505             }
   1506             this._protoValue.add(value);
   1507             return;
   1508         }
   1509         if (!Object.prototype.hasOwnProperty.call(this._map, key)) {
   1510             ++this._size;
   1511             this._map[key] = new Set();
   1512         }
   1513         this._map[key].add(value);
   1514     },
   1515 
   1516     /**
   1517      * @param {string} key
   1518      * @return {!Set.<!T>}
   1519      */
   1520     get: function(key)
   1521     {
   1522         var result = StringMap.prototype.get.call(this, key);
   1523         if (!result)
   1524             result = new Set();
   1525         return result;
   1526     },
   1527 
   1528     /**
   1529      * @param {string} key
   1530      * @param {T} value
   1531      */
   1532     remove: function(key, value)
   1533     {
   1534         var values = this.get(key);
   1535         values.remove(value);
   1536         if (!values.size())
   1537             StringMap.prototype.remove.call(this, key)
   1538     },
   1539 
   1540     /**
   1541      * @param {string} key
   1542      */
   1543     removeAll: function(key)
   1544     {
   1545         StringMap.prototype.remove.call(this, key);
   1546     },
   1547 
   1548     /**
   1549      * @return {!Array.<!T>}
   1550      */
   1551     values: function()
   1552     {
   1553         var result = [];
   1554         var keys = this.keys();
   1555         for (var i = 0; i < keys.length; ++i)
   1556             result.pushAll(this.get(keys[i]).values());
   1557         return result;
   1558     },
   1559 
   1560     __proto__: StringMap.prototype
   1561 }
   1562 
   1563 /**
   1564  * @constructor
   1565  */
   1566 var StringSet = function()
   1567 {
   1568     /** @type {!StringMap.<boolean>} */
   1569     this._map = new StringMap();
   1570 }
   1571 
   1572 /**
   1573  * @param {!Array.<string>} array
   1574  * @return {!StringSet}
   1575  */
   1576 StringSet.fromArray = function(array)
   1577 {
   1578     var result = new StringSet();
   1579     array.forEach(function(item) { result.add(item); });
   1580     return result;
   1581 }
   1582 
   1583 StringSet.prototype = {
   1584     /**
   1585      * @param {string} value
   1586      */
   1587     add: function(value)
   1588     {
   1589         this._map.put(value, true);
   1590     },
   1591 
   1592     /**
   1593      * @param {string} value
   1594      * @return {boolean}
   1595      */
   1596     remove: function(value)
   1597     {
   1598         return !!this._map.remove(value);
   1599     },
   1600 
   1601     /**
   1602      * @return {!Array.<string>}
   1603      */
   1604     values: function()
   1605     {
   1606         return this._map.keys();
   1607     },
   1608 
   1609     /**
   1610      * @param {string} value
   1611      * @return {boolean}
   1612      */
   1613     contains: function(value)
   1614     {
   1615         return this._map.contains(value);
   1616     },
   1617 
   1618     /**
   1619      * @return {number}
   1620      */
   1621     size: function()
   1622     {
   1623         return this._map.size();
   1624     },
   1625 
   1626     clear: function()
   1627     {
   1628         this._map.clear();
   1629     }
   1630 }
   1631 
   1632 /**
   1633  * @param {string} url
   1634  * @param {boolean=} async
   1635  * @param {function(?string)=} callback
   1636  * @return {?string}
   1637  */
   1638 function loadXHR(url, async, callback)
   1639 {
   1640     function onReadyStateChanged()
   1641     {
   1642         if (xhr.readyState !== XMLHttpRequest.DONE)
   1643             return;
   1644 
   1645         if (xhr.status === 200) {
   1646             callback(xhr.responseText);
   1647             return;
   1648         }
   1649 
   1650         callback(null);
   1651    }
   1652 
   1653     var xhr = new XMLHttpRequest();
   1654     xhr.open("GET", url, async);
   1655     if (async)
   1656         xhr.onreadystatechange = onReadyStateChanged;
   1657     xhr.send(null);
   1658 
   1659     if (!async) {
   1660         if (xhr.status === 200)
   1661             return xhr.responseText;
   1662         return null;
   1663     }
   1664     return null;
   1665 }
   1666 
   1667 var _importedScripts = {};
   1668 
   1669 /**
   1670  * @param {string} url
   1671  * @return {string}
   1672  */
   1673 function loadResource(url)
   1674 {
   1675     var xhr = new XMLHttpRequest();
   1676     xhr.open("GET", url, false);
   1677     var stack = new Error().stack;
   1678     try {
   1679         xhr.send(null);
   1680     } catch (e) {
   1681         console.error(url + " -> " + stack);
   1682         throw e;
   1683     }
   1684     return xhr.responseText;
   1685 }
   1686 
   1687 /**
   1688  * This function behavior depends on the "debug_devtools" flag value.
   1689  * - In debug mode it loads scripts synchronously via xhr request.
   1690  * - In release mode every occurrence of "importScript" in the js files
   1691  *   that have been whitelisted in the build system gets replaced with
   1692  *   the script source code on the compilation phase.
   1693  *   The build system will throw an exception if it finds an importScript() call
   1694  *   in other files.
   1695  *
   1696  * To load scripts lazily in release mode call "loadScript" function.
   1697  * @param {string} scriptName
   1698  */
   1699 function importScript(scriptName)
   1700 {
   1701     var sourceURL = self._importScriptPathPrefix + scriptName;
   1702     if (_importedScripts[sourceURL])
   1703         return;
   1704     _importedScripts[sourceURL] = true;
   1705     var scriptSource = loadResource(sourceURL);
   1706     if (!scriptSource)
   1707         throw "empty response arrived for script '" + sourceURL + "'";
   1708     var oldPrefix = self._importScriptPathPrefix;
   1709     self._importScriptPathPrefix += scriptName.substring(0, scriptName.lastIndexOf("/") + 1);
   1710     try {
   1711         self.eval(scriptSource + "\n//# sourceURL=" + sourceURL);
   1712     } finally {
   1713         self._importScriptPathPrefix = oldPrefix;
   1714     }
   1715 }
   1716 
   1717 (function() {
   1718     var baseUrl = location.origin + location.pathname;
   1719     self._importScriptPathPrefix = baseUrl.substring(0, baseUrl.lastIndexOf("/") + 1);
   1720 })();
   1721 
   1722 var loadScript = importScript;
   1723 
   1724 /**
   1725  * @constructor
   1726  */
   1727 function CallbackBarrier()
   1728 {
   1729     this._pendingIncomingCallbacksCount = 0;
   1730 }
   1731 
   1732 CallbackBarrier.prototype = {
   1733     /**
   1734      * @param {function(...)=} userCallback
   1735      * @return {function(...)}
   1736      */
   1737     createCallback: function(userCallback)
   1738     {
   1739         console.assert(!this._outgoingCallback, "CallbackBarrier.createCallback() is called after CallbackBarrier.callWhenDone()");
   1740         ++this._pendingIncomingCallbacksCount;
   1741         return this._incomingCallback.bind(this, userCallback);
   1742     },
   1743 
   1744     /**
   1745      * @param {function()} callback
   1746      */
   1747     callWhenDone: function(callback)
   1748     {
   1749         console.assert(!this._outgoingCallback, "CallbackBarrier.callWhenDone() is called multiple times");
   1750         this._outgoingCallback = callback;
   1751         if (!this._pendingIncomingCallbacksCount)
   1752             this._outgoingCallback();
   1753     },
   1754 
   1755     /**
   1756      * @param {function(...)=} userCallback
   1757      */
   1758     _incomingCallback: function(userCallback)
   1759     {
   1760         console.assert(this._pendingIncomingCallbacksCount > 0);
   1761         if (userCallback) {
   1762             var args = Array.prototype.slice.call(arguments, 1);
   1763             userCallback.apply(null, args);
   1764         }
   1765         if (!--this._pendingIncomingCallbacksCount && this._outgoingCallback)
   1766             this._outgoingCallback();
   1767     }
   1768 }
   1769 
   1770 /**
   1771  * @param {*} value
   1772  */
   1773 function suppressUnused(value)
   1774 {
   1775 }
   1776