1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 * A module that contains basic utility components and methods for the
15 base.debug = function() {};
18 * Whether to break in debugger and alert when an assertion fails.
19 * Set it to true for debugging.
22 base.debug.breakOnAssert = false;
25 * Assert that |expr| is true else print the |opt_msg|.
26 * @param {boolean} expr
27 * @param {string=} opt_msg
29 base.debug.assert = function(expr, opt_msg) {
31 var msg = 'Assertion Failed.';
36 if (base.debug.breakOnAssert) {
44 * @return {string} The callstack of the current method.
46 base.debug.callstack = function() {
50 var error = /** @type {Error} */ e;
51 var callstack = error.stack
52 .replace(/^\s+(at eval )?at\s+/gm, '') // Remove 'at' and indentation.
54 callstack.splice(0,2); // Remove the stack of the current function.
56 return callstack.join('\n');
62 base.Disposable = function() {};
63 base.Disposable.prototype.dispose = function() {};
66 * A utility function to invoke |obj|.dispose without a null check on |obj|.
67 * @param {base.Disposable} obj
69 base.dispose = function(obj) {
71 base.debug.assert(typeof obj.dispose == 'function');
77 * Copy all properties from src to dest.
78 * @param {Object} dest
81 base.mix = function(dest, src) {
82 for (var prop in src) {
83 if (src.hasOwnProperty(prop)) {
84 base.debug.assert(!dest.hasOwnProperty(prop),"Don't override properties");
85 dest[prop] = src[prop];
91 * Adds a mixin to a class.
92 * @param {Object} dest
94 * @suppress {checkTypes}
96 base.extend = function(dest, src) {
97 base.mix(dest.prototype, src.prototype || src);
100 base.doNothing = function() {};
103 * Returns an array containing the values of |dict|.
104 * @param {!Object} dict
107 base.values = function(dict) {
108 return Object.keys(dict).map(
109 /** @param {string} key */
115 base.Promise = function() {};
118 * @param {number} delay
119 * @return {Promise} a Promise that will be fulfilled after |delay| ms.
121 base.Promise.sleep = function(delay) {
123 /** @param {function():void} fulfill */
125 window.setTimeout(fulfill, delay);
131 * @param {Promise} promise
132 * @return {Promise} a Promise that will be fulfilled iff the specified Promise
135 base.Promise.negate = function(promise) {
137 /** @return {Promise} */
139 return Promise.reject();
141 /** @return {Promise} */
143 return Promise.resolve();
148 * A mixin for classes with events.
150 * For example, to create an alarm event for SmokeDetector:
151 * functionSmokeDetector() {
152 * this.defineEvents(['alarm']);
154 * base.extend(SmokeDetector, base.EventSource);
157 * SmokeDetector.prototype.onCarbonMonoxideDetected = function() {
158 * var param = {} // optional parameters
159 * this.raiseEvent('alarm', param);
162 * To listen to an event:
163 * var smokeDetector = new SmokeDetector();
164 * smokeDetector.addEventListener('alarm', listenerObj.someCallback)
169 * Helper interface for the EventSource.
172 base.EventEntry = function() {
173 /** @type {Array.<function():void>} */
179 * Since this class is implemented as a mixin, the constructor may not be
180 * called. All initializations should be done in defineEvents.
182 base.EventSource = function() {
183 /** @type {Object.<string, base.EventEntry>} */
188 * @param {base.EventSource} obj
189 * @param {string} type
191 base.EventSource.isDefined = function(obj, type) {
192 base.debug.assert(Boolean(obj.eventMap_),
193 "The object doesn't support events");
194 base.debug.assert(Boolean(obj.eventMap_[type]), 'Event <' + type +
195 '> is undefined for the current object');
198 base.EventSource.prototype = {
200 * Define |events| for this event source.
201 * @param {Array.<string>} events
203 defineEvents: function(events) {
204 base.debug.assert(!Boolean(this.eventMap_),
205 'defineEvents can only be called once.');
209 * @this {base.EventSource}
210 * @param {string} type
213 base.debug.assert(typeof type == 'string');
214 this.eventMap_[type] = new base.EventEntry();
219 * Add a listener |fn| to listen to |type| event.
220 * @param {string} type
221 * @param {function(?=):void} fn
223 addEventListener: function(type, fn) {
224 base.debug.assert(typeof fn == 'function');
225 base.EventSource.isDefined(this, type);
227 var listeners = this.eventMap_[type].listeners;
232 * Remove the listener |fn| from the event source.
233 * @param {string} type
234 * @param {function(?=):void} fn
236 removeEventListener: function(type, fn) {
237 base.debug.assert(typeof fn == 'function');
238 base.EventSource.isDefined(this, type);
240 var listeners = this.eventMap_[type].listeners;
241 // find the listener to remove.
242 for (var i = 0; i < listeners.length; i++) {
243 var listener = listeners[i];
244 if (listener == fn) {
245 listeners.splice(i, 1);
252 * Fire an event of a particular type on this object.
253 * @param {string} type
254 * @param {*=} opt_details The type of |opt_details| should be ?= to
255 * match what is defined in add(remove)EventListener. However, JSCompile
256 * cannot handle invoking an unknown type as an argument to |listener|
257 * As a hack, we set the type to *=.
259 raiseEvent: function(type, opt_details) {
260 base.EventSource.isDefined(this, type);
262 var entry = this.eventMap_[type];
263 var listeners = entry.listeners.slice(0); // Make a copy of the listeners.
266 /** @param {function(*=):void} listener */
269 listener(opt_details);