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 |