OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
Sergey Ulanov
2014/04/22 00:43:21
lose (c).
2014
kelvinp
2014/04/23 00:24:20
Done.
| |
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 var Debug = {}; | |
Sergey Ulanov
2014/04/22 00:43:21
should this be lower-case 'debug'?
Jamie
2014/04/22 01:23:21
Is there a reason for the case difference between
kelvinp
2014/04/23 00:24:20
debug(fn) is an existing function implemented by c
kelvinp
2014/04/23 00:24:20
Done.
| |
16 | |
17 /** | |
18 * Asserts that expr is true else print the msg | |
Jamie
2014/04/22 01:23:21
s/Asserts/Assert/
kelvinp
2014/04/23 00:24:20
Done.
| |
19 * @param {boolean} expr | |
20 * @param {string=} opt_msg | |
21 */ | |
22 Debug.assert = function (expr, opt_msg) { | |
Jamie
2014/04/22 01:23:21
Nit: No space after "function" (here and elsewhere
kelvinp
2014/04/23 00:24:20
Done.
| |
23 if (!expr) { | |
24 window.alert('Assertion Failed: ' + opt_msg); | |
25 debugger; | |
26 } | |
27 }; | |
28 | |
29 /** | |
30 * @return {string} The callstack of the current method | |
31 * @suppress {checkTypes} | |
Jamie
2014/04/22 01:23:21
Is it possible to make this function type-safe?
kelvinp
2014/04/23 00:24:20
Done.
| |
32 */ | |
33 Debug.callstack = function () { | |
34 var error = null; | |
35 try { | |
36 throw new Error(); | |
37 } catch (e) { | |
38 error = e; | |
39 } | |
40 var callstack = error.stack | |
Jamie
2014/04/22 01:23:21
Why can't this be in the catch block?
kelvinp
2014/04/23 00:24:20
Done.
| |
41 .replace(/^\s+(at eval )?at\s+/gm, '') // remove 'at' and indentation | |
42 .split('\n') | |
43 .splice(0,2); // Remove the stack of the current function | |
44 return callstack.join('\n'); | |
45 }; | |
46 | |
47 /** | |
48 * Called on the dispose method of the obj, without needing to check for | |
49 * whether the object is null | |
50 * @param {Disposable} obj | |
rmsousa
2014/04/22 02:25:35
Disposable isn't base JS. Are you assuming the pre
kelvinp
2014/04/23 00:24:20
No, I am not assuming the presence of any framewor
| |
51 */ | |
52 base.dispose = function (obj) { | |
53 if (Boolean(obj)) { | |
Jamie
2014/04/22 01:23:21
What does the cast to Boolean buy us in this case?
kelvinp
2014/04/23 00:24:20
Trying to pre-emptively make Jscompile happy. I g
| |
54 Debug.assert(typeof obj.dispose == 'function'); | |
55 obj.dispose(); | |
56 } | |
57 }; | |
58 | |
59 /** | |
60 * Copies properties from the src to dest | |
61 * @param {Object} dest | |
62 * @param {Object} src | |
63 */ | |
64 base.mix = function(dest, src) { | |
Sergey Ulanov
2014/04/22 00:43:21
call this copyObjectProperties()?
kelvinp
2014/04/23 00:24:20
I will keep it called mix. I think mix is shorter
| |
65 for (var prop in src) { | |
66 if (src.hasOwnProperty(prop)) { | |
67 Debug.assert(!dest.hasOwnProperty(prop), 'Do not override properties'); | |
68 dest[prop] = src[prop]; | |
69 } | |
70 } | |
71 }; | |
72 | |
73 /** | |
74 * Copies properties from the src to dest.prototype | |
Sergey Ulanov
2014/04/22 00:43:21
I think "mixin" is the standard terms for things l
kelvinp
2014/04/23 00:24:20
Done.
| |
75 * @param {*} dest | |
76 * @param {*} src | |
Jamie
2014/04/22 01:23:21
Why not Object for these two parameters?
kelvinp
2014/04/23 00:24:20
Done.
| |
77 * @suppress {checkTypes} | |
Jamie
2014/04/22 01:23:21
Is it possible to make this function type-safe?
kelvinp
2014/04/23 00:24:20
Unfortunately, the access of src.prototype and des
Jamie
2014/04/23 21:52:57
Agreed, it's probably fine, but I'm surprised jsco
| |
78 */ | |
79 base.augment = function(dest, src) { | |
Sergey Ulanov
2014/04/22 00:43:21
call it base.addClassMixin()? and also rename the
kelvinp
2014/04/23 00:24:20
Augment and extend are the standard terms. I thin
| |
80 base.mix(dest.prototype, src.prototype || src); | |
81 }; | |
82 | |
83 base.emptyFn = function () {}; | |
Sergey Ulanov
2014/04/22 00:43:21
either emptyFunction() or doNothing()
(I would pr
kelvinp
2014/04/23 00:24:20
Done.
| |
84 | |
85 /** | |
86 * A simple implementation to augment JavaScript objects with events | |
Sergey Ulanov
2014/04/22 00:43:21
"Mixin for classes with events."
nit: comments sh
kelvinp
2014/04/23 00:24:20
Done.
| |
87 * | |
88 * For example, to create an alarm event for SmokeDetector | |
89 * function SmokeDetector() { | |
90 * this.defineEvents(['alarm']); | |
91 * }; | |
92 * base.augment(SmokeDetector, base.Events); | |
93 * | |
94 * To fire an event | |
95 * SmokeDetector.prototype.onCarbonMonoxideDetected = function () { | |
96 * var param = {} // optional parameters | |
97 * this.raiseEvent('alarm', param); | |
98 * } | |
99 * | |
100 * To listen to an event | |
101 * var smokeDetector = new SmokeDetector(); | |
102 * smokeDetector.addEventListener('alarm',listenerObj.someCallback,listenerObj); | |
rmsousa
2014/04/22 02:25:35
This mechanism seems a bit dangerous from a lifeti
kelvinp
2014/04/23 00:24:20
Absolutely. You are right in the sense that objec
| |
103 * | |
104 */ | |
105 | |
106 base.Events = function () {}; | |
Jamie
2014/04/22 01:23:21
s/Events/EventSource/? When I read Events, I assum
kelvinp
2014/04/23 00:24:20
Done.
| |
107 | |
108 base.Events.prototype = { | |
109 /** | |
110 * Define the events this event source | |
Sergey Ulanov
2014/04/22 00:43:21
Comments should end with '.'
Jamie
2014/04/22 01:23:21
Incomplete comment.
kelvinp
2014/04/23 00:24:20
Done.
kelvinp
2014/04/23 00:24:20
Done.
| |
111 * @param {Array.<string>} evts | |
112 * @this {EventSource} | |
113 */ | |
114 defineEvents: function (evts) { | |
115 Debug.assert(!Boolean(this._ev), 'defineEvents can only be called once.'); | |
116 var ev = this._ev = {}; | |
Sergey Ulanov
2014/04/22 00:43:21
s/ev/event/
kelvinp
2014/04/23 00:24:20
Done.
| |
117 evts.forEach( | |
118 /** @param {String} type*/ | |
119 function(type) { | |
120 Debug.assert(typeof type == 'string'); | |
121 ev[type] = { | |
Jamie
2014/04/22 01:23:21
The state you're tracking for each event is somewh
kelvinp
2014/04/23 00:24:20
The way that events are handled here is actually a
Jamie
2014/04/23 21:52:57
As discussed, I think either approach to event han
kelvinp
2014/04/24 01:15:39
It is actually not uncommon for one event handler
| |
122 recursionCount: 0, | |
123 sweepRequired: false, | |
124 listeners: [] | |
125 }; | |
126 }); | |
127 }, | |
128 /** | |
129 * Define the events this event source | |
Sergey Ulanov
2014/04/22 00:43:21
Update this comment
Jamie
2014/04/22 01:23:21
Copy/paste error?
kelvinp
2014/04/23 00:24:20
Done.
kelvinp
2014/04/23 00:24:20
Done.
| |
130 * @param {string} type | |
131 * @param {function(Object=)} fn | |
132 * @param {*} thisObj | |
Sergey Ulanov
2014/04/22 00:43:21
Why do we need this argument?
Jamie
2014/04/22 01:23:21
I think it would be better to require callers to p
kelvinp
2014/04/23 00:24:20
Why do you think it is better to require callers t
Jamie
2014/04/23 21:52:57
I think the key word is "necessary". If it's a mem
kelvinp
2014/04/24 01:15:39
Done
| |
133 * @suppress {checkTypes} | |
134 */ | |
135 addEventListener: function (type, fn, thisObj) { | |
136 Debug.assert(typeof fn == 'function'); | |
137 Debug.Events.chk(this, type); | |
138 this._ev[type].listeners.push({ fn: fn, thisObj: thisObj}); | |
139 }, | |
140 /** | |
141 * Define the events this event source | |
Sergey Ulanov
2014/04/22 00:43:21
update the comment
Jamie
2014/04/22 01:23:21
Copy/paste error.
kelvinp
2014/04/23 00:24:20
Done.
kelvinp
2014/04/23 00:24:20
Done.
| |
142 * @param {string} type | |
143 * @param {function(Object=)} fn | |
144 * @param {*} thisObj | |
145 * @suppress {checkTypes} | |
146 */ | |
147 removeEventListener: function (type, fn, thisObj) { | |
148 Debug.assert(typeof fn == 'function'); | |
149 Debug.Events.chk(this, type); | |
150 var listener; | |
Sergey Ulanov
2014/04/22 00:43:21
does this need to be defined outside of the for lo
kelvinp
2014/04/23 00:24:20
Done.
| |
151 var evt = this._ev[type]; | |
152 var listeners = evt.listeners; | |
153 // find the listener to remove | |
154 for (var i = 0; i < listeners.length; i++) { | |
155 listener = listeners[i]; | |
156 if (Boolean(listener) && listener.fn == fn && | |
157 listener.thisObj == thisObj) { | |
158 // if the event is unhooked during raiseEvent, do not remove it | |
159 // immediately, instead set it to null and remove it later. | |
160 if (evt.recursionCount != 0) { | |
161 listeners[i] = null; | |
162 evt.sweepRequired = true; | |
163 } else { | |
164 listeners.splice(i, 1); | |
165 } | |
166 return; | |
Jamie
2014/04/22 01:23:21
In general, I prefer to use break to exit a loop,
kelvinp
2014/04/23 00:24:20
Done.
| |
167 } | |
168 } | |
169 }, | |
170 /** | |
171 * Fire an event of a particular type | |
172 * @param {string} type | |
173 * @param {*} details | |
174 * @suppress {checkTypes} | |
175 */ | |
176 raiseEvent: function (type, details) { | |
177 Debug.Events.chk(this, type); | |
178 var evt = this._ev[type]; | |
179 var listeners = evt.listeners; | |
180 evt.recursionCount++; | |
181 | |
182 for (var i = 0; i < listeners.length; i++) { | |
Jamie
2014/04/22 01:23:21
Can you use forEach here?
kelvinp
2014/04/23 00:24:20
Done.
| |
183 var listener = listeners[i]; | |
184 if (listener) { | |
185 listener.fn.call(listener.thisObj, details); | |
186 } | |
187 } | |
188 | |
189 evt.recursionCount--; | |
190 if (evt.recursionCount == 0 && evt.sweepRequired) { | |
191 for (i = listeners.length; i--;) { | |
Sergey Ulanov
2014/04/22 00:43:21
The following would me more readable:
for (var i
kelvinp
2014/04/23 00:24:20
javascript is function scope. var is already decl
| |
192 if (!listeners[i]) { | |
Sergey Ulanov
2014/04/22 00:43:21
Why do you need this check?
kelvinp
2014/04/23 00:24:20
Comments added
| |
193 listeners.splice(i, 1); | |
194 } | |
195 } | |
196 evt.sweepRequired = false; | |
197 } | |
198 } | |
199 }; | |
200 | |
201 Debug.Events = { | |
202 /** | |
203 * @param {EventSource} obj | |
204 * @param {string} type | |
205 */ | |
206 chk: function (obj, type) { | |
Sergey Ulanov
2014/04/22 00:43:21
Nt rdbl nm.
(not readable name)
kelvinp
2014/04/23 00:24:20
Done.
| |
207 Debug.assert(Boolean(obj._ev), 'The object doesn\'t support events'); | |
Sergey Ulanov
2014/04/22 00:43:21
nit: double space
Jamie
2014/04/22 01:23:21
For strings with embedded apostrophes, you can use
kelvinp
2014/04/23 00:24:20
Done.
kelvinp
2014/04/23 00:24:20
Done.
| |
208 Debug.assert(Boolean(obj._ev[type]), 'Event <' + type + | |
209 '> is undefined for the current object'); | |
210 } | |
211 }; | |
212 | |
213 // Turns off assert in production. Comment out the line below for debugging. | |
214 Debug.assert = base.emptyFn; | |
Sergey Ulanov
2014/04/22 00:43:21
Why not have asserts always on? IMO it's better to
kelvinp
2014/04/23 00:24:20
Done.
| |
OLD | NEW |