2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 var base = base || {};
30 base.endsWith = function(string, suffix)
32 if (suffix.length > string.length)
34 var expectedIndex = string.length - suffix.length;
35 return string.lastIndexOf(suffix) == expectedIndex;
38 base.joinPath = function(parent, child)
40 if (parent.length == 0)
42 return parent + '/' + child;
45 base.dirName = function(path)
47 var directoryIndex = path.lastIndexOf('/');
48 if (directoryIndex == -1)
50 return path.substr(0, directoryIndex);
53 base.trimExtension = function(url)
55 var index = url.lastIndexOf('.');
58 return url.substr(0, index);
61 base.uniquifyArray = function(array)
65 $.each(array, function(index, value) {
74 base.flattenArray = function(arrayOfArrays)
76 if (!arrayOfArrays.length)
78 return arrayOfArrays.reduce(function(left, right) {
79 return left.concat(right);
83 base.filterDictionary = function(dictionary, predicate)
87 for (var key in dictionary) {
89 result[key] = dictionary[key];
95 base.mapDictionary = function(dictionary, functor)
99 for (var key in dictionary) {
100 var value = functor(dictionary[key]);
101 if (typeof value !== 'undefined')
108 base.filterTree = function(tree, isLeaf, predicate)
110 var filteredTree = {};
112 function walkSubtree(subtree, directory)
114 for (var childName in subtree) {
115 var child = subtree[childName];
116 var childPath = base.joinPath(directory, childName);
118 if (predicate(child))
119 filteredTree[childPath] = child;
122 walkSubtree(child, childPath);
126 walkSubtree(tree, '');
130 base.forEachDirectory = function(pathList, callback)
132 var pathsByDirectory = {};
133 pathList.forEach(function(path) {
134 var directory = base.dirName(path);
135 pathsByDirectory[directory] = pathsByDirectory[directory] || [];
136 pathsByDirectory[directory].push(path);
138 Object.keys(pathsByDirectory).sort().forEach(function(directory) {
139 var paths = pathsByDirectory[directory];
140 callback(directory + ' (' + paths.length + ' tests)', paths);
144 base.parseJSONP = function(jsonp)
149 if (!jsonp.match(/^[^{[]*\(/))
150 return JSON.parse(jsonp);
152 var startIndex = jsonp.indexOf('(') + 1;
153 var endIndex = jsonp.lastIndexOf(')');
154 if (startIndex == 0 || endIndex == -1)
156 return JSON.parse(jsonp.substr(startIndex, endIndex - startIndex));
159 // This is effectively a cache of possibly-resolved promises.
160 base.AsynchronousCache = function(fetch)
163 this._promiseCache = {};
166 base.AsynchronousCache._sentinel = new Object();
167 base.AsynchronousCache.prototype.get = function(key)
169 if (!(key in this._promiseCache)) {
170 this._promiseCache[key] = base.AsynchronousCache._sentinel;
171 this._promiseCache[key] = this._fetch.call(null, key);
173 if (this._promiseCache[key] === base.AsynchronousCache._sentinel)
174 return Promise.reject(Error("Reentrant request for ", key));
176 return this._promiseCache[key];
179 base.AsynchronousCache.prototype.clear = function()
181 this._promiseCache = {};
185 Maintains a dictionary of items, tracking their updates and removing items that haven't been updated.
186 An "update" is a call to the "update" method.
187 To remove stale items, call the "remove" method. It will remove all
188 items that have not been been updated since the last call of "remove".
190 base.UpdateTracker = function()
196 base.UpdateTracker.prototype = {
198 Update an {key}/{item} pair. You can make the dictionary act as a set and
199 skip the {item}, in which case the {key} is also the {item}.
201 update: function(key, object)
203 object = object || key;
204 this._items[key] = object;
205 this._updated[key] = 1;
207 exists: function(key)
209 return !!this.get(key);
213 return this._items[key];
217 return Object.keys(this._items).length;
220 Callback parameters are:
223 - updated, which is true if the item was updated after last purge() call.
225 forEach: function(callback, thisObject)
230 Object.keys(this._items).sort().forEach(function(key) {
231 var item = this._items[key];
232 callback.call(thisObject || item, item, key, !!this._updated[key]);
235 purge: function(removeCallback, thisObject) {
236 removeCallback = removeCallback || function() {};
237 this.forEach(function(item, key, updated) {
240 removeCallback.call(thisObject || item, item);
241 delete this._items[key];
247 // Based on http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/resources/shared/js/cr/ui.js
248 base.extends = function(base, prototype)
250 var extended = function() {
251 var element = typeof base == 'string' ? document.createElement(base) : base.call(this);
252 extended.prototype.__proto__ = element.__proto__;
253 element.__proto__ = extended.prototype;
254 var singleton = element.init && element.init.apply(element, arguments);
260 extended.prototype = prototype;
264 base.getURLParameter = function(name)
266 var match = RegExp(name + '=' + '(.+?)(&|$)').exec(location.search);
269 return decodeURI(match[1])
272 base.underscoredBuilderName = function(builderName)
274 return builderName.replace(/[ .()]/g, '_');