Chromium Code Reviews| 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 |