OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 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 base.debug = function () {}; |
| 16 |
| 17 /** |
| 18 * Whether to break in debugger and alert when an assertion fails. |
| 19 * Set it to true for debugging. |
| 20 * @type {boolean} |
| 21 */ |
| 22 base.debug.breakOnAssert = false; |
| 23 |
| 24 /** |
| 25 * Assert that |expr| is true else print the |opt_msg|. |
| 26 * @param {boolean} expr |
| 27 * @param {string=} opt_msg |
| 28 */ |
| 29 base.debug.assert = function(expr, opt_msg) { |
| 30 if (!expr) { |
| 31 var msg = 'Assertion Failed.'; |
| 32 if (opt_msg) { |
| 33 msg += ' ' + opt_msg; |
| 34 } |
| 35 console.error(msg); |
| 36 if (base.debug.breakOnAssert) { |
| 37 alert(msg); |
| 38 debugger; |
| 39 } |
| 40 } |
| 41 }; |
| 42 |
| 43 /** |
| 44 * @return {string} The callstack of the current method. |
| 45 */ |
| 46 base.debug.callstack = function() { |
| 47 try { |
| 48 throw new Error(); |
| 49 } catch (e) { |
| 50 var error = /** @type {Error} */ e; |
| 51 var callstack = error.stack |
| 52 .replace(/^\s+(at eval )?at\s+/gm, '') // Remove 'at' and indentation. |
| 53 .split('\n') |
| 54 .splice(0,2); // Remove the stack of the current function. |
| 55 } |
| 56 return callstack.join('\n'); |
| 57 }; |
| 58 |
| 59 /** |
| 60 * @interface |
| 61 */ |
| 62 base.Disposable = function() {}; |
| 63 base.Disposable.prototype.dispose = function() {}; |
| 64 |
| 65 /** |
| 66 * A utility function to invoke |obj|.dispose without a null check on |obj|. |
| 67 * @param {base.Disposable} obj |
| 68 */ |
| 69 base.dispose = function(obj) { |
| 70 if (obj) { |
| 71 base.debug.assert(typeof obj.dispose == 'function'); |
| 72 obj.dispose(); |
| 73 } |
| 74 }; |
| 75 |
| 76 /** |
| 77 * Copy all properties from src to dest. |
| 78 * @param {Object} dest |
| 79 * @param {Object} src |
| 80 */ |
| 81 base.mix = function(dest, src) { |
| 82 for (var prop in src) { |
| 83 if (src.hasOwnProperty(prop)) { |
| 84 base.debug.assert(!dest.hasOwnProperty(prop),"Don't override properties"); |
| 85 dest[prop] = src[prop]; |
| 86 } |
| 87 } |
| 88 }; |
| 89 |
| 90 /** |
| 91 * Adds a mixin to a class. |
| 92 * @param {Object} dest |
| 93 * @param {Object} src |
| 94 * @suppress {checkTypes} |
| 95 */ |
| 96 base.extend = function(dest, src) { |
| 97 base.mix(dest.prototype, src.prototype || src); |
| 98 }; |
| 99 |
| 100 base.doNothing = function() {}; |
| 101 |
| 102 /** |
| 103 * A mixin for classes with events. |
| 104 * |
| 105 * For example, to create an alarm event for SmokeDetector: |
| 106 * functionSmokeDetector() { |
| 107 * this.defineEvents(['alarm']); |
| 108 * }; |
| 109 * base.extend(SmokeDetector, base.EventSource); |
| 110 * |
| 111 * To fire an event: |
| 112 * SmokeDetector.prototype.onCarbonMonoxideDetected = function() { |
| 113 * var param = {} // optional parameters |
| 114 * this.raiseEvent('alarm', param); |
| 115 * } |
| 116 * |
| 117 * To listen to an event: |
| 118 * var smokeDetector = new SmokeDetector(); |
| 119 * smokeDetector.addEventListener('alarm', listenerObj.someCallback) |
| 120 * |
| 121 */ |
| 122 |
| 123 /** |
| 124 * Helper interface for the EventSource. |
| 125 * @constructor |
| 126 */ |
| 127 base.EventEntry = function() { |
| 128 /** @type {Array.<function():void>} */ |
| 129 this.listeners = []; |
| 130 }; |
| 131 |
| 132 /** |
| 133 * @constructor |
| 134 * Since this class is implemented as a mixin, the constructor may not be |
| 135 * called. All initializations should be done in defineEvents. |
| 136 */ |
| 137 base.EventSource = function() { |
| 138 /** @type {Object.<string, base.EventEntry>} */ |
| 139 this.eventMap_; |
| 140 }; |
| 141 |
| 142 /** |
| 143 * @param {base.EventSource} obj |
| 144 * @param {string} type |
| 145 */ |
| 146 base.EventSource.isDefined = function(obj, type) { |
| 147 base.debug.assert(Boolean(obj.eventMap_), |
| 148 "The object doesn't support events"); |
| 149 base.debug.assert(Boolean(obj.eventMap_[type]), 'Event <' + type + |
| 150 '> is undefined for the current object'); |
| 151 }; |
| 152 |
| 153 base.EventSource.prototype = { |
| 154 /** |
| 155 * Define |events| for this event source. |
| 156 * @param {Array.<string>} events |
| 157 */ |
| 158 defineEvents: function(events) { |
| 159 base.debug.assert(!Boolean(this.eventMap_), |
| 160 'defineEvents can only be called once.'); |
| 161 this.eventMap_ = {}; |
| 162 events.forEach( |
| 163 /** |
| 164 * @this {base.EventSource} |
| 165 * @param {string} type |
| 166 */ |
| 167 function(type) { |
| 168 base.debug.assert(typeof type == 'string'); |
| 169 this.eventMap_[type] = new base.EventEntry(); |
| 170 }, this); |
| 171 }, |
| 172 |
| 173 /** |
| 174 * Add a listener |fn| to listen to |type| event. |
| 175 * @param {string} type |
| 176 * @param {function(?=):void} fn |
| 177 */ |
| 178 addEventListener: function(type, fn) { |
| 179 base.debug.assert(typeof fn == 'function'); |
| 180 base.EventSource.isDefined(this, type); |
| 181 |
| 182 var listeners = this.eventMap_[type].listeners; |
| 183 listeners.push(fn); |
| 184 }, |
| 185 |
| 186 /** |
| 187 * Remove the listener |fn| from the event source. |
| 188 * @param {string} type |
| 189 * @param {function(?=):void} fn |
| 190 */ |
| 191 removeEventListener: function(type, fn) { |
| 192 base.debug.assert(typeof fn == 'function'); |
| 193 base.EventSource.isDefined(this, type); |
| 194 |
| 195 var listeners = this.eventMap_[type].listeners; |
| 196 // find the listener to remove. |
| 197 for (var i = 0; i < listeners.length; i++) { |
| 198 var listener = listeners[i]; |
| 199 if (listener == fn) { |
| 200 listeners.splice(i, 1); |
| 201 break; |
| 202 } |
| 203 } |
| 204 }, |
| 205 |
| 206 /** |
| 207 * Fire an event of a particular type on this object. |
| 208 * @param {string} type |
| 209 * @param {*=} opt_details The type of |opt_details| should be ?= to |
| 210 * match what is defined in add(remove)EventListener. However, JSCompile |
| 211 * cannot handle invoking an unknown type as an argument to |listener| |
| 212 * As a hack, we set the type to *=. |
| 213 */ |
| 214 raiseEvent: function(type, opt_details) { |
| 215 base.EventSource.isDefined(this, type); |
| 216 |
| 217 var entry = this.eventMap_[type]; |
| 218 var listeners = entry.listeners.slice(0); // Make a copy of the listeners. |
| 219 |
| 220 listeners.forEach( |
| 221 /** @param {function(*=):void} listener */ |
| 222 function(listener){ |
| 223 if (listener) { |
| 224 listener(opt_details); |
| 225 } |
| 226 }); |
| 227 } |
| 228 }; |
OLD | NEW |