Chromium Code Reviews| Index: remoting/webapp/base.js |
| diff --git a/remoting/webapp/base.js b/remoting/webapp/base.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..074c13f61bd5422e76aeacfcec431713b6271362 |
| --- /dev/null |
| +++ b/remoting/webapp/base.js |
| @@ -0,0 +1,214 @@ |
| +// 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.
|
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +/** |
| + * @fileoverview |
| + * A module that contains basic utility components and methods for the |
| + * chromoting project |
| + * |
| + */ |
| + |
| +'use strict'; |
| + |
| +var base = {}; |
| +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.
|
| + |
| +/** |
| + * 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.
|
| + * @param {boolean} expr |
| + * @param {string=} opt_msg |
| + */ |
| +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.
|
| + if (!expr) { |
| + window.alert('Assertion Failed: ' + opt_msg); |
| + debugger; |
| + } |
| +}; |
| + |
| +/** |
| + * @return {string} The callstack of the current method |
| + * @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.
|
| + */ |
| +Debug.callstack = function () { |
| + var error = null; |
| + try { |
| + throw new Error(); |
| + } catch (e) { |
| + error = e; |
| + } |
| + 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.
|
| + .replace(/^\s+(at eval )?at\s+/gm, '') // remove 'at' and indentation |
| + .split('\n') |
| + .splice(0,2); // Remove the stack of the current function |
| + return callstack.join('\n'); |
| +}; |
| + |
| +/** |
| + * Called on the dispose method of the obj, without needing to check for |
| + * whether the object is null |
| + * @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
|
| + */ |
| +base.dispose = function (obj) { |
| + 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
|
| + Debug.assert(typeof obj.dispose == 'function'); |
| + obj.dispose(); |
| + } |
| +}; |
| + |
| +/** |
| + * Copies properties from the src to dest |
| + * @param {Object} dest |
| + * @param {Object} src |
| + */ |
| +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
|
| + for (var prop in src) { |
| + if (src.hasOwnProperty(prop)) { |
| + Debug.assert(!dest.hasOwnProperty(prop), 'Do not override properties'); |
| + dest[prop] = src[prop]; |
| + } |
| + } |
| +}; |
| + |
| +/** |
| + * 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.
|
| + * @param {*} dest |
| + * @param {*} src |
|
Jamie
2014/04/22 01:23:21
Why not Object for these two parameters?
kelvinp
2014/04/23 00:24:20
Done.
|
| + * @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
|
| + */ |
| +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
|
| + base.mix(dest.prototype, src.prototype || src); |
| +}; |
| + |
| +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.
|
| + |
| +/** |
| + * 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.
|
| + * |
| + * For example, to create an alarm event for SmokeDetector |
| + * function SmokeDetector() { |
| + * this.defineEvents(['alarm']); |
| + * }; |
| + * base.augment(SmokeDetector, base.Events); |
| + * |
| + * To fire an event |
| + * SmokeDetector.prototype.onCarbonMonoxideDetected = function () { |
| + * var param = {} // optional parameters |
| + * this.raiseEvent('alarm', param); |
| + * } |
| + * |
| + * To listen to an event |
| + * var smokeDetector = new SmokeDetector(); |
| + * 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
|
| + * |
| + */ |
| + |
| +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.
|
| + |
| +base.Events.prototype = { |
| + /** |
| + * 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.
|
| + * @param {Array.<string>} evts |
| + * @this {EventSource} |
| + */ |
| + defineEvents: function (evts) { |
| + Debug.assert(!Boolean(this._ev), 'defineEvents can only be called once.'); |
| + var ev = this._ev = {}; |
|
Sergey Ulanov
2014/04/22 00:43:21
s/ev/event/
kelvinp
2014/04/23 00:24:20
Done.
|
| + evts.forEach( |
| + /** @param {String} type*/ |
| + function(type) { |
| + Debug.assert(typeof type == 'string'); |
| + 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
|
| + recursionCount: 0, |
| + sweepRequired: false, |
| + listeners: [] |
| + }; |
| + }); |
| + }, |
| + /** |
| + * 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.
|
| + * @param {string} type |
| + * @param {function(Object=)} fn |
| + * @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
|
| + * @suppress {checkTypes} |
| + */ |
| + addEventListener: function (type, fn, thisObj) { |
| + Debug.assert(typeof fn == 'function'); |
| + Debug.Events.chk(this, type); |
| + this._ev[type].listeners.push({ fn: fn, thisObj: thisObj}); |
| + }, |
| + /** |
| + * 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.
|
| + * @param {string} type |
| + * @param {function(Object=)} fn |
| + * @param {*} thisObj |
| + * @suppress {checkTypes} |
| + */ |
| + removeEventListener: function (type, fn, thisObj) { |
| + Debug.assert(typeof fn == 'function'); |
| + Debug.Events.chk(this, type); |
| + 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.
|
| + var evt = this._ev[type]; |
| + var listeners = evt.listeners; |
| + // find the listener to remove |
| + for (var i = 0; i < listeners.length; i++) { |
| + listener = listeners[i]; |
| + if (Boolean(listener) && listener.fn == fn && |
| + listener.thisObj == thisObj) { |
| + // if the event is unhooked during raiseEvent, do not remove it |
| + // immediately, instead set it to null and remove it later. |
| + if (evt.recursionCount != 0) { |
| + listeners[i] = null; |
| + evt.sweepRequired = true; |
| + } else { |
| + listeners.splice(i, 1); |
| + } |
| + 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.
|
| + } |
| + } |
| + }, |
| + /** |
| + * Fire an event of a particular type |
| + * @param {string} type |
| + * @param {*} details |
| + * @suppress {checkTypes} |
| + */ |
| + raiseEvent: function (type, details) { |
| + Debug.Events.chk(this, type); |
| + var evt = this._ev[type]; |
| + var listeners = evt.listeners; |
| + evt.recursionCount++; |
| + |
| + 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.
|
| + var listener = listeners[i]; |
| + if (listener) { |
| + listener.fn.call(listener.thisObj, details); |
| + } |
| + } |
| + |
| + evt.recursionCount--; |
| + if (evt.recursionCount == 0 && evt.sweepRequired) { |
| + 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
|
| + 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
|
| + listeners.splice(i, 1); |
| + } |
| + } |
| + evt.sweepRequired = false; |
| + } |
| + } |
| +}; |
| + |
| +Debug.Events = { |
| + /** |
| + * @param {EventSource} obj |
| + * @param {string} type |
| + */ |
| + 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.
|
| + 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.
|
| + Debug.assert(Boolean(obj._ev[type]), 'Event <' + type + |
| + '> is undefined for the current object'); |
| + } |
| +}; |
| + |
| +// Turns off assert in production. Comment out the line below for debugging. |
| +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.
|