tizen beta release
[profile/ivi/webkit-efl.git] / Tools / BuildSlaveSupport / build.webkit.org-config / public_html / TestFailures / scripts / base.js
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 var base = base || {};
27
28 (function(){
29
30 // Safari 5.1 lacks Function.prototype.bind.
31 if (!('bind' in Function.prototype)) {
32     Function.prototype.bind = function(thisObject) {
33         var method = this;
34         var boundArguments = [];
35         for (var i = 1; i < arguments.length; ++i) {
36             boundArguments.push(arguments[i]);
37         }
38         return function() {
39             var actualParameters = [];
40             for (var i = 0; i < boundArguments.length; ++i) {
41                 actualParameters.push(boundArguments[i]);
42             }
43             for (var i = 0; i < arguments.length; ++i) {
44                 actualParameters.push(arguments[i]);
45             }
46             return method.apply(thisObject, actualParameters);
47         }
48     }
49 }
50
51 base.asInteger = function(stringOrInteger)
52 {
53     if (typeof stringOrInteger == 'string')
54         return parseInt(stringOrInteger);
55     return stringOrInteger;
56 };
57
58 base.endsWith = function(string, suffix)
59 {
60     if (suffix.length > string.length)
61         return false;
62     var expectedIndex = string.length - suffix.length;
63     return string.lastIndexOf(suffix) == expectedIndex;
64 };
65
66 base.repeatString = function(string, count)
67 {
68     return new Array(count + 1).join(string);
69 };
70
71 base.joinPath = function(parent, child)
72 {
73     if (parent.length == 0)
74         return child;
75     return parent + '/' + child;
76 };
77
78 base.dirName = function(path)
79 {
80     var directoryIndex = path.lastIndexOf('/');
81     if (directoryIndex == -1)
82         return path;
83     return path.substr(0, directoryIndex);
84 };
85
86 base.trimExtension = function(url)
87 {
88     var index = url.lastIndexOf('.');
89     if (index == -1)
90         return url;
91     return url.substr(0, index);
92 }
93
94 base.uniquifyArray = function(array)
95 {
96     var seen = {};
97     var result = [];
98     $.each(array, function(index, value) {
99         if (seen[value])
100             return;
101         seen[value] = true;
102         result.push(value);
103     });
104     return result;
105 };
106
107 base.flattenArray = function(arrayOfArrays)
108 {
109     if (!arrayOfArrays.length)
110         return [];
111     return arrayOfArrays.reduce(function(left, right) {
112         return left.concat(right);  
113     });
114 };
115
116 base.values = function(dictionary)
117 {
118     var result = [];
119
120     for (var key in dictionary) {
121         result.push(dictionary[key]);
122     }
123
124     return result;
125 };
126
127 base.filterDictionary = function(dictionary, predicate)
128 {
129     var result = {};
130
131     for (var key in dictionary) {
132         if (predicate(key))
133             result[key] = dictionary[key];
134     }
135
136     return result;
137 };
138
139 base.mapDictionary = function(dictionary, functor)
140 {
141     var result = {};
142
143     for (var key in dictionary) {
144         var value = functor(dictionary[key]);
145         if (typeof value !== 'undefined')
146             result[key] = value;
147     }
148
149     return result;
150 };
151
152 base.filterTree = function(tree, isLeaf, predicate)
153 {
154     var filteredTree = {};
155
156     function walkSubtree(subtree, directory)
157     {
158         for (var childName in subtree) {
159             var child = subtree[childName];
160             var childPath = base.joinPath(directory, childName);
161             if (isLeaf(child)) {
162                 if (predicate(child))
163                     filteredTree[childPath] = child;
164                 continue;
165             }
166             walkSubtree(child, childPath);
167         }
168     }
169
170     walkSubtree(tree, '');
171     return filteredTree;
172 };
173
174 base.parseJSONP = function(jsonp)
175 {
176     var startIndex = jsonp.indexOf('(') + 1;
177     var endIndex = jsonp.lastIndexOf(')');
178     return JSON.parse(jsonp.substr(startIndex, endIndex - startIndex));
179 }
180
181 base.RequestTracker = function(requestsInFlight, callback, args)
182 {
183     this._requestsInFlight = requestsInFlight;
184     this._callback = callback;
185     this._args = args || [];
186     this._tryCallback();
187 };
188
189 base.RequestTracker.prototype = {
190     _tryCallback: function()
191     {
192         if (!this._requestsInFlight && this._callback)
193             this._callback.apply(null, this._args);
194     },
195     requestComplete: function()
196     {
197         --this._requestsInFlight;
198         this._tryCallback();
199     }
200 }
201
202 base.callInParallel = function(functionList, callback)
203 {
204     var requestTracker = new base.RequestTracker(functionList.length, callback);
205
206     $.each(functionList, function(index, func) {
207         func(function() {
208             requestTracker.requestComplete();
209         });
210     });
211 };
212
213 base.callInSequence = function(func, argumentList, callback)
214 {
215     var nextIndex = 0;
216
217     function callNext()
218     {
219         if (nextIndex >= argumentList.length) {
220             callback();
221             return;
222         }
223
224         func(argumentList[nextIndex++], callNext);
225     }
226
227     callNext();
228 };
229
230 base.CallbackIterator = function(callback, listOfArgumentArrays)
231 {
232     this._callback = callback;
233     this._nextIndex = 0;
234     this._listOfArgumentArrays = listOfArgumentArrays;
235 };
236
237 base.CallbackIterator.prototype.hasNext = function()
238 {
239     return this._nextIndex < this._listOfArgumentArrays.length;
240 };
241
242 base.CallbackIterator.prototype.hasPrevious = function()
243 {
244     return this._nextIndex - 2 >= 0;
245 };
246
247 base.CallbackIterator.prototype.callNext = function()
248 {
249     if (!this.hasNext())
250         return;
251     var args = this._listOfArgumentArrays[this._nextIndex];
252     this._nextIndex++;
253     this._callback.apply(null, args);
254 };
255
256 base.CallbackIterator.prototype.callPrevious = function()
257 {
258     if (!this.hasPrevious())
259         return;
260     var args = this._listOfArgumentArrays[this._nextIndex - 2];
261     this._nextIndex--;
262     this._callback.apply(null, args);
263 };
264
265 base.AsynchronousCache = function(fetch)
266 {
267     this._fetch = fetch;
268     this._dataCache = {};
269     this._callbackCache = {};
270 };
271
272 base.AsynchronousCache.prototype.get = function(key, callback)
273 {
274     var self = this;
275
276     if (self._dataCache[key]) {
277         // FIXME: Consider always calling callback asynchronously.
278         callback(self._dataCache[key]);
279         return;
280     }
281
282     if (key in self._callbackCache) {
283         self._callbackCache[key].push(callback);
284         return;
285     }
286
287     self._callbackCache[key] = [callback];
288
289     self._fetch.call(null, key, function(data) {
290         self._dataCache[key] = data;
291
292         var callbackList = self._callbackCache[key];
293         delete self._callbackCache[key];
294
295         callbackList.forEach(function(cachedCallback) {
296             cachedCallback(data);
297         });
298     });
299 };
300
301 /*
302     Maintains a dictionary of items, tracking their updates and removing items that haven't been updated.
303     An "update" is a call to the "update" method.
304     To remove stale items, call the "remove" method. It will remove all
305     items that have not been been updated since the last call of "remove".
306 */
307 base.UpdateTracker = function()
308 {
309     this._items = {};
310     this._updated = {};
311 }
312
313 base.UpdateTracker.prototype = {
314     /*
315         Update an {key}/{item} pair. You can make the dictionary act as a set and
316         skip the {item}, in which case the {key} is also the {item}.
317     */
318     update: function(key, object)
319     {
320         object = object || key;
321         this._items[key] = object;
322         this._updated[key] = 1;
323     },
324     exists: function(key)
325     {
326         return !!this.get(key);
327     },
328     get: function(key)
329     {
330         return this._items[key];
331     },
332     length: function()
333     {
334         return Object.keys(this._items).length;
335     },
336     /*
337         Callback parameters are:
338         - item
339         - key
340         - updated, which is true if the item was updated after last purge() call.
341     */
342     forEach: function(callback, thisObject)
343     {
344         if (!callback)
345             return;
346
347         Object.keys(this._items).sort().forEach(function(key) {
348             var item = this._items[key];
349             callback.call(thisObject || item, item, key, !!this._updated[key]);
350         }, this);
351     },
352     purge: function(removeCallback, thisObject) {
353         removeCallback = removeCallback || function() {};
354         this.forEach(function(item, key, updated) {
355             if (updated)
356                 return;
357             removeCallback.call(thisObject || item, item);
358             delete this._items[key];
359         }, this);
360         this._updated = {};
361     }
362 }
363
364 // Based on http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/resources/shared/js/cr/ui.js
365 base.extends = function(base, prototype)
366 {
367     var extended = function() {
368         var element = typeof base == 'string' ? document.createElement(base) : base.call(this);
369         extended.prototype.__proto__ = element.__proto__;
370         element.__proto__ = extended.prototype;
371         element.init && element.init.apply(element, arguments);
372         return element;
373     }
374
375     extended.prototype = prototype;
376     return extended;
377 }
378
379 function createRelativeTimeDescriptor(divisorInMilliseconds, unit)
380 {
381     return function(delta) {
382         var deltaInUnits = delta / divisorInMilliseconds;
383         return (deltaInUnits).toFixed(0) + ' ' + unit + (deltaInUnits >= 1.5 ? 's' : '') + ' ago';
384     }
385 }
386
387 var kMinuteInMilliseconds = 60 * 1000;
388 var kRelativeTimeSlots = [
389     {
390         maxMilliseconds: kMinuteInMilliseconds,
391         describe: function(delta) { return 'Just now'; }
392     },
393     {
394         maxMilliseconds: 60 * kMinuteInMilliseconds,
395         describe: createRelativeTimeDescriptor(kMinuteInMilliseconds, 'minute')
396     },
397     {
398         maxMilliseconds: 24 * 60 * kMinuteInMilliseconds,
399         describe: createRelativeTimeDescriptor(60 * kMinuteInMilliseconds, 'hour')
400     },
401     {
402         maxMilliseconds: Number.MAX_VALUE,
403         describe: createRelativeTimeDescriptor(24 * 60 * kMinuteInMilliseconds, 'day')
404     }
405 ];
406
407 /*
408     Represent time as descriptive text, relative to now and gradually decreasing in precision:
409         delta < 1 minutes => Just Now
410         delta < 60 minutes => X minute[s] ago
411         delta < 24 hours => X hour[s] ago
412         delta < inf => X day[s] ago
413 */
414 base.relativizeTime = function(time)
415 {
416     var result;
417     var delta = new Date().getTime() - time;
418     kRelativeTimeSlots.some(function(slot) {
419         if (delta >= slot.maxMilliseconds)
420             return false;
421
422         result = slot.describe(delta);
423         return true;
424     });
425     return result;
426 }
427
428 })();