Pipeline: Handle the case where Seek() is called after error happened.
[chromium-blink-merge.git] / remoting / webapp / base.js
blob4771e53714a1ca25bc858485f95159525aaf0d1b
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.
5 /**
6  * @fileoverview
7  * A module that contains basic utility components and methods for the
8  * chromoting project
9  *
10  */
12 'use strict';
14 var base = {};
15 base.debug = function() {};
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;
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   }
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     callstack.splice(0,2); // Remove the stack of the current function.
55   }
56   return callstack.join('\n');
59 /**
60   * @interface
61   */
62 base.Disposable = function() {};
63 base.Disposable.prototype.dispose = function() {};
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   }
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   }
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);
100 base.doNothing = function() {};
103  * Returns an array containing the values of |dict|.
104  * @param {!Object} dict
105  * @return {Array}
106  */
107 base.values = function(dict) {
108   return Object.keys(dict).map(
109     /** @param {string} key */
110     function(key) {
111       return dict[key];
112     });
115 base.Promise = function() {};
118  * @param {number} delay
119  * @return {Promise} a Promise that will be fulfilled after |delay| ms.
120  */
121 base.Promise.sleep = function(delay) {
122   return new Promise(
123     /** @param {function():void} fulfill */
124     function(fulfill) {
125       window.setTimeout(fulfill, delay);
126     });
131  * @param {Promise} promise
132  * @return {Promise} a Promise that will be fulfilled iff the specified Promise
133  *     is rejected.
134  */
135 base.Promise.negate = function(promise) {
136   return promise.then(
137       /** @return {Promise} */
138       function() {
139         return Promise.reject();
140       },
141       /** @return {Promise} */
142       function() {
143         return Promise.resolve();
144       });
148  * A mixin for classes with events.
150  * For example, to create an alarm event for SmokeDetector:
151  * functionSmokeDetector() {
152  *    this.defineEvents(['alarm']);
153  * };
154  * base.extend(SmokeDetector, base.EventSource);
156  * To fire an event:
157  * SmokeDetector.prototype.onCarbonMonoxideDetected = function() {
158  *   var param = {} // optional parameters
159  *   this.raiseEvent('alarm', param);
160  * }
162  * To listen to an event:
163  * var smokeDetector = new SmokeDetector();
164  * smokeDetector.addEventListener('alarm', listenerObj.someCallback)
166  */
169   * Helper interface for the EventSource.
170   * @constructor
171   */
172 base.EventEntry = function() {
173   /** @type {Array.<function():void>} */
174   this.listeners = [];
178   * @constructor
179   * Since this class is implemented as a mixin, the constructor may not be
180   * called.  All initializations should be done in defineEvents.
181   */
182 base.EventSource = function() {
183   /** @type {Object.<string, base.EventEntry>} */
184   this.eventMap_;
188   * @param {base.EventSource} obj
189   * @param {string} type
190   */
191 base.EventSource.isDefined = function(obj, type) {
192   base.debug.assert(Boolean(obj.eventMap_),
193                    "The object doesn't support events");
194   base.debug.assert(Boolean(obj.eventMap_[type]), 'Event <' + type +
195     '> is undefined for the current object');
198 base.EventSource.prototype = {
199   /**
200     * Define |events| for this event source.
201     * @param {Array.<string>} events
202     */
203   defineEvents: function(events) {
204     base.debug.assert(!Boolean(this.eventMap_),
205                      'defineEvents can only be called once.');
206     this.eventMap_ = {};
207     events.forEach(
208       /**
209         * @this {base.EventSource}
210         * @param {string} type
211         */
212       function(type) {
213         base.debug.assert(typeof type == 'string');
214         this.eventMap_[type] = new base.EventEntry();
215     }, this);
216   },
218   /**
219     * Add a listener |fn| to listen to |type| event.
220     * @param {string} type
221     * @param {function(?=):void} fn
222     */
223   addEventListener: function(type, fn) {
224     base.debug.assert(typeof fn == 'function');
225     base.EventSource.isDefined(this, type);
227     var listeners = this.eventMap_[type].listeners;
228     listeners.push(fn);
229   },
231   /**
232     * Remove the listener |fn| from the event source.
233     * @param {string} type
234     * @param {function(?=):void} fn
235     */
236   removeEventListener: function(type, fn) {
237     base.debug.assert(typeof fn == 'function');
238     base.EventSource.isDefined(this, type);
240     var listeners = this.eventMap_[type].listeners;
241     // find the listener to remove.
242     for (var i = 0; i < listeners.length; i++) {
243       var listener = listeners[i];
244       if (listener == fn) {
245         listeners.splice(i, 1);
246         break;
247       }
248     }
249   },
251   /**
252     * Fire an event of a particular type on this object.
253     * @param {string} type
254     * @param {*=} opt_details The type of |opt_details| should be ?= to
255     *     match what is defined in add(remove)EventListener.  However, JSCompile
256     *     cannot handle invoking an unknown type as an argument to |listener|
257     *     As a hack, we set the type to *=.
258     */
259   raiseEvent: function(type, opt_details) {
260     base.EventSource.isDefined(this, type);
262     var entry = this.eventMap_[type];
263     var listeners = entry.listeners.slice(0); // Make a copy of the listeners.
265     listeners.forEach(
266       /** @param {function(*=):void} listener */
267       function(listener){
268         if (listener) {
269           listener(opt_details);
270         }
271     });
272   }