Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(389)

Unified Diff: remoting/webapp/base.js

Issue 245923002: Bring events to JavaScript components (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add base.js and Event Listeners Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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.

Powered by Google App Engine
This is Rietveld 408576698