Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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. | |
| 4 | |
| 5 /** | |
| 6 * @fileoverview | |
| 7 * A module that contains basic utility components and methods for the | |
| 8 * chromoting project | |
| 9 * | |
| 10 */ | |
| 11 | |
| 12 'use strict'; | |
| 13 | |
| 14 var base = {}; | |
| 15 base.debug = function () {}; | |
| 16 | |
| 17 /** | |
| 18 * Whether to break in debugger and alert when an assertion fails. | |
| 19 * Set it to true for debugging. | |
| 20 * @type {boolean} | |
| 21 */ | |
| 22 base.debug.breakOnAssert = false; | |
| 23 | |
| 24 /** | |
| 25 * Assert that |expr| is true else print the |opt_msg|. | |
| 26 * @param {boolean} expr | |
| 27 * @param {string=} opt_msg | |
| 28 */ | |
| 29 base.debug.assert = function(expr, opt_msg) { | |
| 30 if (!expr) { | |
| 31 var msg = 'Assertion Failed.'; | |
| 32 if (opt_msg) { | |
| 33 msg += ' ' + opt_msg; | |
| 34 } | |
| 35 console.error(msg); | |
| 36 if (base.debug.breakOnAssert) { | |
| 37 alert(msg); | |
| 38 debugger; | |
| 39 } | |
| 40 } | |
| 41 }; | |
| 42 | |
| 43 /** | |
| 44 * @return {string} The callstack of the current method. | |
| 45 */ | |
| 46 base.debug.callstack = function() { | |
| 47 try { | |
| 48 throw new Error(); | |
| 49 } catch (e) { | |
| 50 var error = /** @type {Error} */ e; | |
| 51 var callstack = error.stack | |
| 52 .replace(/^\s+(at eval )?at\s+/gm, '') // Remove 'at' and indentation. | |
| 53 .split('\n') | |
| 54 .splice(0,2); // Remove the stack of the current function. | |
| 55 } | |
| 56 return callstack.join('\n'); | |
| 57 }; | |
| 58 | |
| 59 /** | |
| 60 * @interface | |
| 61 */ | |
| 62 base.Disposable = function() {}; | |
| 63 base.Disposable.prototype.dispose = function() {}; | |
| 64 | |
| 65 /** | |
| 66 * A utility function to invoke |obj|.dispose without a null check on |obj|. | |
| 67 * @param {base.Disposable} obj | |
| 68 */ | |
| 69 base.dispose = function(obj) { | |
| 70 if (obj) { | |
| 71 base.debug.assert(typeof obj.dispose == 'function'); | |
| 72 obj.dispose(); | |
| 73 } | |
| 74 }; | |
| 75 | |
| 76 /** | |
| 77 * Copy all properties from src to dest. | |
| 78 * @param {Object} dest | |
| 79 * @param {Object} src | |
| 80 */ | |
| 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]; | |
| 86 } | |
| 87 } | |
| 88 }; | |
| 89 | |
| 90 /** | |
| 91 * Adds a mixin to a class. | |
| 92 * @param {Object} dest | |
| 93 * @param {Object} src | |
| 94 * @suppress {checkTypes} | |
| 95 */ | |
| 96 base.extend = function(dest, src) { | |
| 97 base.mix(dest.prototype, src.prototype || src); | |
| 98 }; | |
| 99 | |
| 100 base.doNothing = function() {}; | |
| 101 | |
| 102 /** | |
| 103 * A mixin for classes with events. | |
| 104 * | |
| 105 * For example, to create an alarm event for SmokeDetector: | |
| 106 * functionSmokeDetector() { | |
| 107 * this.defineEvents(['alarm']); | |
| 108 * }; | |
| 109 * base.extend(SmokeDetector, base.EventSource); | |
| 110 * | |
| 111 * To fire an event: | |
| 112 * SmokeDetector.prototype.onCarbonMonoxideDetected = function() { | |
| 113 * var param = {} // optional parameters | |
| 114 * this.raiseEvent('alarm', param); | |
| 115 * } | |
| 116 * | |
| 117 * To listen to an event: | |
| 118 * var smokeDetector = new SmokeDetector(); | |
| 119 * smokeDetector.addEventListener('alarm', listenerObj.someCallback) | |
| 120 * | |
| 121 */ | |
| 122 | |
| 123 /** | |
| 124 * Helper interface for the EventSource. | |
| 125 * @interface | |
| 126 */ | |
| 127 base.EventEntry = function() { | |
| 128 /** @type {Array.<Function>} */ | |
|
Jamie
2014/04/24 20:38:34
Is "Function" a synonym for "function()"?
kelvinp
2014/04/24 21:38:32
Done.
| |
| 129 this.listeners; | |
| 130 }; | |
| 131 | |
| 132 /** @constructor */ | |
| 133 base.EventSource = function() { | |
| 134 this.eventMap_ = {}; | |
|
Jamie
2014/04/24 20:38:34
Add type? I think it should be Object.<string, Eve
kelvinp
2014/04/24 21:38:32
Done.
| |
| 135 }; | |
| 136 | |
| 137 /** | |
| 138 * @param {base.EventSource} obj | |
| 139 * @param {string} type | |
| 140 */ | |
| 141 base.EventSource.checkType = function(obj, type) { | |
|
Jamie
2014/04/24 20:38:34
Why not make this a regular member function? Also,
kelvinp
2014/04/24 21:38:32
Renamed to isDefined. Don't want to make it a reg
| |
| 142 base.debug.assert(Boolean(obj.eventMap_), | |
| 143 "The object doesn't support events"); | |
| 144 base.debug.assert(Boolean(obj.eventMap_[type]), 'Event <' + type + | |
| 145 '> is undefined for the current object'); | |
| 146 }; | |
| 147 base.EventSource.prototype = { | |
| 148 /** | |
| 149 * Define |events| for this event source. | |
| 150 * @param {Array.<string>} events | |
| 151 */ | |
| 152 defineEvents: function(events) { | |
| 153 base.debug.assert(!Boolean(this.eventMap_), | |
| 154 'defineEvents can only be called once.'); | |
| 155 this.eventMap_ = {}; | |
| 156 events.forEach( | |
| 157 /** | |
| 158 * @this {base.EventSource} | |
| 159 * @param {string} type | |
| 160 */ | |
| 161 function(type) { | |
| 162 base.debug.assert(typeof type == 'string'); | |
| 163 this.eventMap_[type] = { | |
|
Jamie
2014/04/24 20:38:34
I think you want "new EventEntry" here.
kelvinp
2014/04/24 21:38:32
Don't know how I miss that. Good catch
| |
| 164 recursionCount: 0, | |
| 165 sweepRequired: false, | |
| 166 listeners: [] | |
| 167 }; | |
| 168 }, this); | |
| 169 }, | |
| 170 /** | |
|
Wez
2014/04/24 20:45:15
nit: I prefer a blank line between the }, and the
kelvinp
2014/04/24 21:38:32
Done.
| |
| 171 * Add a listener |fn| to listen to |type| event. The listener |fn| will be | |
| 172 * invoked with |thisObj| as the this pointer. | |
|
Jamie
2014/04/24 20:38:34
thisObj no longer exists.
kelvinp
2014/04/24 21:38:32
Done.
| |
| 173 * @param {string} type | |
| 174 * @param {function(?=)} fn | |
|
Jamie
2014/04/24 20:38:34
No need for "?=" in the type, here and below.
kelvinp
2014/04/24 21:38:32
Spoke offline. I want to indicate that the parame
| |
| 175 */ | |
| 176 addEventListener: function(type, fn) { | |
| 177 base.debug.assert(typeof fn == 'function'); | |
| 178 base.EventSource.checkType(this, type); | |
| 179 | |
| 180 var listeners = /** @type {Array} */ this.eventMap_[type].listeners; | |
|
Jamie
2014/04/24 20:38:34
This cast should not be necessary if you add @type
kelvinp
2014/04/24 21:38:32
Done.
| |
| 181 listeners.push(fn); | |
| 182 }, | |
| 183 /** | |
| 184 * Remove the listener |fn| from the event source. | |
| 185 * @param {string} type | |
| 186 * @param {function(?=)} fn | |
| 187 */ | |
| 188 removeEventListener: function(type, fn) { | |
| 189 base.debug.assert(typeof fn == 'function'); | |
| 190 base.EventSource.checkType(this, type); | |
| 191 | |
| 192 /** @type {base.EventEntry} */ | |
| 193 var entry = this.eventMap_[type]; | |
| 194 | |
| 195 var listeners = entry.listeners; | |
| 196 // find the listener to remove. | |
| 197 for (var i = 0; i < listeners.length; i++) { | |
| 198 var listener = listeners[i]; | |
| 199 if (listener && listener == fn) { | |
| 200 listeners.splice(i, 1); | |
| 201 break; | |
| 202 } | |
| 203 } | |
| 204 }, | |
| 205 /** | |
| 206 * Fire an event of a particular type on this object. | |
| 207 * @param {string} type | |
| 208 * @param {*} details | |
| 209 */ | |
| 210 raiseEvent: function(type, details) { | |
| 211 base.EventSource.checkType(this, type); | |
| 212 | |
| 213 /** @type {base.EventEntry} */ | |
| 214 var entry = this.eventMap_[type]; | |
| 215 var listeners = entry.listeners.slice(0); // Make a copy of the listeners. | |
| 216 | |
| 217 listeners.forEach( | |
| 218 /** @param {Function} listener */ | |
| 219 function(listener){ | |
| 220 if (listener) { | |
| 221 listener(details); | |
| 222 } | |
| 223 }); | |
| 224 } | |
| 225 }; | |
| OLD | NEW |