1<!DOCTYPE html>
2<!--
3Copyright (c) 2014 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/tracing/base/base.html">
9
10<script>
11'use strict';
12
13tr.exportTo('tr.b', function() {
14  function asArray(arrayish) {
15    var values = [];
16    for (var i = 0; i < arrayish.length; i++)
17      values.push(arrayish[i]);
18    return values;
19  }
20
21  function compareArrays(x, y, elementCmp) {
22    var minLength = Math.min(x.length, y.length);
23    for (var i = 0; i < minLength; i++) {
24      var tmp = elementCmp(x[i], y[i]);
25      if (tmp)
26        return tmp;
27    }
28    if (x.length == y.length)
29      return 0;
30
31    if (x[i] === undefined)
32      return -1;
33
34    return 1;
35  }
36
37  /**
38   * Compares two values when one or both might be undefined. Undefined
39   * values are sorted after defined.
40   */
41  function comparePossiblyUndefinedValues(x, y, cmp, opt_this) {
42    if (x !== undefined && y !== undefined)
43      return cmp.call(opt_this, x, y);
44    if (x !== undefined)
45      return -1;
46    if (y !== undefined)
47      return 1;
48    return 0;
49  }
50
51  /**
52   * Compares two numeric values when one or both might be undefined or NaNs.
53   * Undefined / NaN values are sorted after others.
54   */
55  function compareNumericWithNaNs(x, y) {
56    if (!isNaN(x) && !isNaN(y))
57      return x - y;
58    if (isNaN(x))
59      return 1;
60    if (isNaN(y))
61      return -1;
62    return 0;
63  }
64
65  function concatenateArrays(/*arguments*/) {
66    var values = [];
67    for (var i = 0; i < arguments.length; i++) {
68      if (!(arguments[i] instanceof Array))
69        throw new Error('Arguments ' + i + 'is not an array');
70      values.push.apply(values, arguments[i]);
71    }
72    return values;
73  }
74
75  function concatenateObjects(/*arguments*/) {
76    var result = {};
77    for (var i = 0; i < arguments.length; i++) {
78      var object = arguments[i];
79      for (var j in object) {
80        result[j] = object[j];
81      }
82    }
83    return result;
84  }
85
86  function dictionaryKeys(dict) {
87    var keys = [];
88    for (var key in dict)
89      keys.push(key);
90    return keys;
91  }
92
93  function dictionaryValues(dict) {
94    var values = [];
95    for (var key in dict)
96      values.push(dict[key]);
97    return values;
98  }
99
100  function dictionaryLength(dict) {
101    var n = 0;
102    for (var key in dict)
103      n++;
104    return n;
105  }
106
107  function dictionaryContainsValue(dict, value) {
108    for (var key in dict)
109      if (dict[key] === value)
110        return true;
111    return false;
112  }
113
114  /**
115   * Returns a new dictionary with items grouped by the return value of the
116   * specified function being called on each item.
117   * @param {!Array.<Object>} ary The array being iterated through
118   * @param {!Function} fn The mapping function between the array value and the
119   * map key.
120   */
121  function group(ary, fn) {
122    return ary.reduce(function(accumulator, curr) {
123      var key = fn(curr);
124
125      if (key in accumulator)
126        accumulator[key].push(curr);
127      else
128        accumulator[key] = [curr];
129
130      return accumulator;
131    }, {});
132  }
133
134  function iterItems(dict, fn, opt_this) {
135    opt_this = opt_this || this;
136    var keys = Object.keys(dict);
137    for (var i = 0; i < keys.length; i++) {
138      var key = keys[i];
139      fn.call(opt_this, key, dict[key]);
140    }
141  }
142
143  /**
144   * Create a new dictionary with the same keys as the original dictionary
145   * mapped to the results of the provided function called on the corresponding
146   * entries in the original dictionary, i.e. result[key] = fn(key, dict[key])
147   * for all keys in dict (own enumerable properties only).
148   *
149   * Example:
150   *   var srcDict = {a: 10, b: 15};
151   *   var dstDict = mapItems(srcDict, function(k, v) { return 2 * v; });
152   *   // srcDict is unmodified and dstDict is now equal to {a: 20, b: 30}.
153   */
154  function mapItems(dict, fn, opt_this) {
155    opt_this = opt_this || this;
156    var result = {};
157    var keys = Object.keys(dict);
158    for (var i = 0; i < keys.length; i++) {
159      var key = keys[i];
160      result[key] = fn.call(opt_this, key, dict[key]);
161    }
162    return result;
163  }
164
165  function filterItems(dict, predicate, opt_this) {
166    opt_this = opt_this || this;
167    var result = {};
168    var keys = Object.keys(dict);
169    for (var i = 0; i < keys.length; i++) {
170      var key = keys[i];
171      var value = dict[key];
172      if (predicate.call(opt_this, key, value))
173        result[key] = value;
174    }
175    return result;
176  }
177
178  function iterObjectFieldsRecursively(object, func) {
179    if (!(object instanceof Object))
180      return;
181
182    if (object instanceof Array) {
183      for (var i = 0; i < object.length; i++) {
184        func(object, i, object[i]);
185        iterObjectFieldsRecursively(object[i], func);
186      }
187      return;
188    }
189
190    for (var key in object) {
191      var value = object[key];
192      func(object, key, value);
193      iterObjectFieldsRecursively(value, func);
194    }
195  }
196
197  /**
198   * Convert an array of dictionaries to a dictionary of arrays.
199   *
200   * The keys of the resulting dictionary are a union of the keys of all
201   * dictionaries in the provided array. Each array in the resulting dictionary
202   * has the same length as the provided array and contains the values of its
203   * key in the dictionaries in the provided array. Example:
204   *
205   *   INPUT:
206   *
207   *     [
208   *       {a: 6, b: 5      },
209   *       undefined,
210   *       {a: 4, b: 3, c: 2},
211   *       {      b: 1, c: 0}
212   *     ]
213   *
214   *   OUTPUT:
215   *
216   *     {
217   *       a: [6,         undefined, 4, undefined],
218   *       b: [5,         undefined, 3, 1        ],
219   *       c: [undefined, undefined, 2, 0        ]
220   *     }
221   *
222   * @param {!Array} array Array of items to be inverted. If opt_dictGetter
223   *     is not provided, all elements of the array must be either undefined,
224   *     or dictionaries.
225   * @param {?(function(*): (!Object|undefined))=} opt_dictGetter Optional
226   *     function mapping defined elements of array to dictionaries.
227   * @param {*=} opt_this Optional 'this' context for opt_dictGetter.
228   */
229  function invertArrayOfDicts(array, opt_dictGetter, opt_this) {
230    opt_this = opt_this || this;
231    var result = {};
232    for (var i = 0; i < array.length; i++) {
233      var item = array[i];
234      if (item === undefined)
235        continue;
236      var dict = opt_dictGetter ? opt_dictGetter.call(opt_this, item) : item;
237      if (dict === undefined)
238        continue;
239      for (var key in dict) {
240        var valueList = result[key];
241        if (valueList === undefined)
242          result[key] = valueList = new Array(array.length);
243        valueList[i] = dict[key];
244      }
245    }
246    return result;
247  }
248
249  /**
250   * Convert an array to a dictionary.
251   *
252   * Every element in the array is mapped in the dictionary to the key returned
253   * by the provided function:
254   *
255   *   dictionary[valueToKeyFn(element)] = element;
256   *
257   * @param {!Array} array Arbitrary array.
258   * @param {function(*): string} valueToKeyFn Function mapping array elements
259   *     to dictionary keys.
260   * @param {*=} opt_this Optional 'this' context for valueToKeyFn.
261   */
262  function arrayToDict(array, valueToKeyFn, opt_this) {
263    opt_this = opt_this || this;
264    var result = {};
265    var length = array.length;
266    for (var i = 0; i < length; i++) {
267      var value = array[i];
268      var key = valueToKeyFn.call(opt_this, value);
269      result[key] = value;
270    }
271    return result;
272  }
273
274  function identity(d) {
275    return d;
276  }
277
278  function findFirstIndexInArray(ary, opt_func, opt_this) {
279    var func = opt_func || identity;
280    for (var i = 0; i < ary.length; i++) {
281      if (func.call(opt_this, ary[i], i))
282        return i;
283    }
284    return -1;
285  }
286
287  function findFirstInArray(ary, opt_func, opt_this) {
288    var i = findFirstIndexInArray(ary, opt_func, opt_func);
289    if (i === -1)
290      return undefined;
291    return ary[i];
292  }
293
294  function findFirstKeyInDictMatching(dict, opt_func, opt_this) {
295    var func = opt_func || identity;
296    for (var key in dict) {
297      if (func.call(opt_this, key, dict[key]))
298        return key;
299    }
300    return undefined;
301  }
302
303  function mapValues(map) {
304    var values = [];
305    for (var value of map.values())
306      values.push(value);
307    return values;
308  }
309
310  function iterMapItems(map, fn, opt_this) {
311    opt_this = opt_this || this;
312    for (var key of map.keys())
313      fn.call(opt_this, key, map.get(key));
314  }
315
316  return {
317    asArray: asArray,
318    concatenateArrays: concatenateArrays,
319    concatenateObjects: concatenateObjects,
320    compareArrays: compareArrays,
321    comparePossiblyUndefinedValues: comparePossiblyUndefinedValues,
322    compareNumericWithNaNs: compareNumericWithNaNs,
323    dictionaryLength: dictionaryLength,
324    dictionaryKeys: dictionaryKeys,
325    dictionaryValues: dictionaryValues,
326    dictionaryContainsValue: dictionaryContainsValue,
327    group: group,
328    iterItems: iterItems,
329    mapItems: mapItems,
330    filterItems: filterItems,
331    iterObjectFieldsRecursively: iterObjectFieldsRecursively,
332    invertArrayOfDicts: invertArrayOfDicts,
333    arrayToDict: arrayToDict,
334    identity: identity,
335    findFirstIndexInArray: findFirstIndexInArray,
336    findFirstInArray: findFirstInArray,
337    findFirstKeyInDictMatching: findFirstKeyInDictMatching,
338    mapValues: mapValues,
339    iterMapItems: iterMapItems
340  };
341});
342</script>
343