summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorkelvinp@chromium.org <kelvinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-25 08:55:18 +0000
committerkelvinp@chromium.org <kelvinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-25 08:55:18 +0000
commitf8881a71968e1b5f1af981a3196d43e25217c312 (patch)
tree467c6bb4f678c475365e0437acf91f7b6f251e73 /remoting
parent13d0975c431fe6b070c776e01bc2b651b291bcd6 (diff)
downloadchromium_src-f8881a71968e1b5f1af981a3196d43e25217c312.zip
chromium_src-f8881a71968e1b5f1af981a3196d43e25217c312.tar.gz
chromium_src-f8881a71968e1b5f1af981a3196d43e25217c312.tar.bz2
A simple implementation to augment JavaScript objects with events
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); The code lives in (base.js), a module that contains JavaScript utility components and methods for the web app. Review URL: https://codereview.chromium.org/245923002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266164 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/remoting_webapp_files.gypi5
-rw-r--r--remoting/webapp/all_js_load.gtestjs1
-rw-r--r--remoting/webapp/base.js228
3 files changed, 233 insertions, 1 deletions
diff --git a/remoting/remoting_webapp_files.gypi b/remoting/remoting_webapp_files.gypi
index cc94f6e..287442e 100644
--- a/remoting/remoting_webapp_files.gypi
+++ b/remoting/remoting_webapp_files.gypi
@@ -51,6 +51,7 @@
],
# Remoting core JavaScript files.
'remoting_webapp_js_core_files': [
+ 'webapp/base.js',
'webapp/error.js',
'webapp/event_handlers.js',
'webapp/plugin_settings.js',
@@ -117,10 +118,12 @@
],
# The JavaScript files required by main.html.
'remoting_webapp_main_html_js_files': [
+ # Include the core files first as it is required by the other files.
+ # Otherwise, Jscompile will complain.
+ '<@(remoting_webapp_js_core_files)',
'<@(remoting_webapp_js_auth_client2host_files)',
'<@(remoting_webapp_js_auth_google_files)',
'<@(remoting_webapp_js_client_files)',
- '<@(remoting_webapp_js_core_files)',
'<@(remoting_webapp_js_gnubby_auth_files)',
'<@(remoting_webapp_js_host_files)',
'<@(remoting_webapp_js_logging_files)',
diff --git a/remoting/webapp/all_js_load.gtestjs b/remoting/webapp/all_js_load.gtestjs
index 0ab7cf8..6eedd3e 100644
--- a/remoting/webapp/all_js_load.gtestjs
+++ b/remoting/webapp/all_js_load.gtestjs
@@ -15,6 +15,7 @@ AllJsLoadTest.prototype = {
/** @inheritDoc */
extraLibraries: [
+ 'base.js',
'browser_globals.gtestjs',
// All of our Javascript files should be listed here unless they are
// only used by JSCompiler
diff --git a/remoting/webapp/base.js b/remoting/webapp/base.js
new file mode 100644
index 0000000..19d574d
--- /dev/null
+++ b/remoting/webapp/base.js
@@ -0,0 +1,228 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// 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 = {};
+base.debug = function () {};
+
+/**
+ * Whether to break in debugger and alert when an assertion fails.
+ * Set it to true for debugging.
+ * @type {boolean}
+ */
+base.debug.breakOnAssert = false;
+
+/**
+ * Assert that |expr| is true else print the |opt_msg|.
+ * @param {boolean} expr
+ * @param {string=} opt_msg
+ */
+base.debug.assert = function(expr, opt_msg) {
+ if (!expr) {
+ var msg = 'Assertion Failed.';
+ if (opt_msg) {
+ msg += ' ' + opt_msg;
+ }
+ console.error(msg);
+ if (base.debug.breakOnAssert) {
+ alert(msg);
+ debugger;
+ }
+ }
+};
+
+/**
+ * @return {string} The callstack of the current method.
+ */
+base.debug.callstack = function() {
+ try {
+ throw new Error();
+ } catch (e) {
+ var error = /** @type {Error} */ e;
+ var callstack = error.stack
+ .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');
+};
+
+/**
+ * @interface
+ */
+base.Disposable = function() {};
+base.Disposable.prototype.dispose = function() {};
+
+/**
+ * A utility function to invoke |obj|.dispose without a null check on |obj|.
+ * @param {base.Disposable} obj
+ */
+base.dispose = function(obj) {
+ if (obj) {
+ base.debug.assert(typeof obj.dispose == 'function');
+ obj.dispose();
+ }
+};
+
+/**
+ * Copy all properties from src to dest.
+ * @param {Object} dest
+ * @param {Object} src
+ */
+base.mix = function(dest, src) {
+ for (var prop in src) {
+ if (src.hasOwnProperty(prop)) {
+ base.debug.assert(!dest.hasOwnProperty(prop),"Don't override properties");
+ dest[prop] = src[prop];
+ }
+ }
+};
+
+/**
+ * Adds a mixin to a class.
+ * @param {Object} dest
+ * @param {Object} src
+ * @suppress {checkTypes}
+ */
+base.extend = function(dest, src) {
+ base.mix(dest.prototype, src.prototype || src);
+};
+
+base.doNothing = function() {};
+
+/**
+ * A mixin for classes with events.
+ *
+ * For example, to create an alarm event for SmokeDetector:
+ * functionSmokeDetector() {
+ * this.defineEvents(['alarm']);
+ * };
+ * base.extend(SmokeDetector, base.EventSource);
+ *
+ * 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)
+ *
+ */
+
+/**
+ * Helper interface for the EventSource.
+ * @constructor
+ */
+base.EventEntry = function() {
+ /** @type {Array.<function():void>} */
+ this.listeners = [];
+};
+
+/**
+ * @constructor
+ * Since this class is implemented as a mixin, the constructor may not be
+ * called. All initializations should be done in defineEvents.
+ */
+base.EventSource = function() {
+ /** @type {Object.<string, base.EventEntry>} */
+ this.eventMap_;
+};
+
+/**
+ * @param {base.EventSource} obj
+ * @param {string} type
+ */
+base.EventSource.isDefined = function(obj, type) {
+ base.debug.assert(Boolean(obj.eventMap_),
+ "The object doesn't support events");
+ base.debug.assert(Boolean(obj.eventMap_[type]), 'Event <' + type +
+ '> is undefined for the current object');
+};
+
+base.EventSource.prototype = {
+ /**
+ * Define |events| for this event source.
+ * @param {Array.<string>} events
+ */
+ defineEvents: function(events) {
+ base.debug.assert(!Boolean(this.eventMap_),
+ 'defineEvents can only be called once.');
+ this.eventMap_ = {};
+ events.forEach(
+ /**
+ * @this {base.EventSource}
+ * @param {string} type
+ */
+ function(type) {
+ base.debug.assert(typeof type == 'string');
+ this.eventMap_[type] = new base.EventEntry();
+ }, this);
+ },
+
+ /**
+ * Add a listener |fn| to listen to |type| event.
+ * @param {string} type
+ * @param {function(?=):void} fn
+ */
+ addEventListener: function(type, fn) {
+ base.debug.assert(typeof fn == 'function');
+ base.EventSource.isDefined(this, type);
+
+ var listeners = this.eventMap_[type].listeners;
+ listeners.push(fn);
+ },
+
+ /**
+ * Remove the listener |fn| from the event source.
+ * @param {string} type
+ * @param {function(?=):void} fn
+ */
+ removeEventListener: function(type, fn) {
+ base.debug.assert(typeof fn == 'function');
+ base.EventSource.isDefined(this, type);
+
+ var listeners = this.eventMap_[type].listeners;
+ // find the listener to remove.
+ for (var i = 0; i < listeners.length; i++) {
+ var listener = listeners[i];
+ if (listener == fn) {
+ listeners.splice(i, 1);
+ break;
+ }
+ }
+ },
+
+ /**
+ * Fire an event of a particular type on this object.
+ * @param {string} type
+ * @param {*=} opt_details The type of |opt_details| should be ?= to
+ * match what is defined in add(remove)EventListener. However, JSCompile
+ * cannot handle invoking an unknown type as an argument to |listener|
+ * As a hack, we set the type to *=.
+ */
+ raiseEvent: function(type, opt_details) {
+ base.EventSource.isDefined(this, type);
+
+ var entry = this.eventMap_[type];
+ var listeners = entry.listeners.slice(0); // Make a copy of the listeners.
+
+ listeners.forEach(
+ /** @param {function(*=):void} listener */
+ function(listener){
+ if (listener) {
+ listener(opt_details);
+ }
+ });
+ }
+};