Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / remoting / webapp / base.js
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     callstack.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  * 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     });
113 };
114
115 /**
116  * A mixin for classes with events.
117  *
118  * For example, to create an alarm event for SmokeDetector:
119  * functionSmokeDetector() {
120  *    this.defineEvents(['alarm']);
121  * };
122  * base.extend(SmokeDetector, base.EventSource);
123  *
124  * To fire an event:
125  * SmokeDetector.prototype.onCarbonMonoxideDetected = function() {
126  *   var param = {} // optional parameters
127  *   this.raiseEvent('alarm', param);
128  * }
129  *
130  * To listen to an event:
131  * var smokeDetector = new SmokeDetector();
132  * smokeDetector.addEventListener('alarm', listenerObj.someCallback)
133  *
134  */
135
136 /**
137   * Helper interface for the EventSource.
138   * @constructor
139   */
140 base.EventEntry = function() {
141   /** @type {Array.<function():void>} */
142   this.listeners = [];
143 };
144
145 /**
146   * @constructor
147   * Since this class is implemented as a mixin, the constructor may not be
148   * called.  All initializations should be done in defineEvents.
149   */
150 base.EventSource = function() {
151   /** @type {Object.<string, base.EventEntry>} */
152   this.eventMap_;
153 };
154
155 /**
156   * @param {base.EventSource} obj
157   * @param {string} type
158   */
159 base.EventSource.isDefined = function(obj, type) {
160   base.debug.assert(Boolean(obj.eventMap_),
161                    "The object doesn't support events");
162   base.debug.assert(Boolean(obj.eventMap_[type]), 'Event <' + type +
163     '> is undefined for the current object');
164 };
165
166 base.EventSource.prototype = {
167   /**
168     * Define |events| for this event source.
169     * @param {Array.<string>} events
170     */
171   defineEvents: function(events) {
172     base.debug.assert(!Boolean(this.eventMap_),
173                      'defineEvents can only be called once.');
174     this.eventMap_ = {};
175     events.forEach(
176       /**
177         * @this {base.EventSource}
178         * @param {string} type
179         */
180       function(type) {
181         base.debug.assert(typeof type == 'string');
182         this.eventMap_[type] = new base.EventEntry();
183     }, this);
184   },
185
186   /**
187     * Add a listener |fn| to listen to |type| event.
188     * @param {string} type
189     * @param {function(?=):void} fn
190     */
191   addEventListener: 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     listeners.push(fn);
197   },
198
199   /**
200     * Remove the listener |fn| from the event source.
201     * @param {string} type
202     * @param {function(?=):void} fn
203     */
204   removeEventListener: function(type, fn) {
205     base.debug.assert(typeof fn == 'function');
206     base.EventSource.isDefined(this, type);
207
208     var listeners = this.eventMap_[type].listeners;
209     // find the listener to remove.
210     for (var i = 0; i < listeners.length; i++) {
211       var listener = listeners[i];
212       if (listener == fn) {
213         listeners.splice(i, 1);
214         break;
215       }
216     }
217   },
218
219   /**
220     * Fire an event of a particular type on this object.
221     * @param {string} type
222     * @param {*=} opt_details The type of |opt_details| should be ?= to
223     *     match what is defined in add(remove)EventListener.  However, JSCompile
224     *     cannot handle invoking an unknown type as an argument to |listener|
225     *     As a hack, we set the type to *=.
226     */
227   raiseEvent: function(type, opt_details) {
228     base.EventSource.isDefined(this, type);
229
230     var entry = this.eventMap_[type];
231     var listeners = entry.listeners.slice(0); // Make a copy of the listeners.
232
233     listeners.forEach(
234       /** @param {function(*=):void} listener */
235       function(listener){
236         if (listener) {
237           listener(opt_details);
238         }
239     });
240   }
241 };