Initial commit
[profile/ivi/SettingsApp.git] / js / websocket.js
1 /*
2  * Copyright (c) 2013, Intel Corporation.
3  *
4  * This program is licensed under the terms and conditions of the
5  * Apache License, version 2.0.  The full text of the Apache License is at
6  * http://www.apache.org/licenses/LICENSE-2.0
7  *
8  */
9
10 /* Dummy backend for faking websocket daemon */
11 var dummyBackend = true;
12
13 var WS_REQUEST_TYPE = {
14             WIFI : 0,
15             BLUETOOTH : 1,
16             DISPLAY : 2,
17             SOUND: 3,
18             DATETIME: 4,
19             LOCALE: 5
20         };
21
22 var WS_EVENT_TYPE = {
23             WIFI : 0,
24             BLUETOOTH : 1,
25             DISPLAY : 2,
26             SOUND: 3,
27             DATETIME: 4,
28             LOCALE: 5
29         };
30
31 /* web socket module to connect to the settings daemon */
32 var wsAPI = (function() {
33     /* number of connection retries to attempt if the socket closes */
34     var self = this;
35     this.connected = false;
36         this.event_callbacks = $.Callbacks();
37
38     /* default values for WebSocket */
39     this.socketUrl = 'ws://localhost:16000/settings';
40     this.socketProtocol = 'http-only';
41
42     this.methodIdx = 0;
43     this.methodCalls = [];
44     for (var i = 0; i < 100; i++) {
45         this.methodCalls[i] = null;
46     }
47
48     this.MethodCall = function(id, name, success_cb, error_cb) {
49         var me = this;
50         this.successCB = success_cb;
51         this.errorCB = error_cb;
52         this.transactionid = id;
53         this.name = name;
54         this.done = false;
55         this.start = function() {
56             me.timeout = setTimeout(function() {
57                 if (me.errorCB !== undefined) {
58                     me.errorCB('\"' + me.name + '\" method timed out after ' + self.timeouttime + 'ms');
59                 }
60                 me.finish();
61             }, self.timeouttime);
62         }
63         this.finish = function() {
64             if (me.timeout !== undefined) {
65                 clearTimeout(me.timeout);
66             }
67             me.done = true;
68         }
69     }
70     
71     this.EventObject = function(type, name, value) {
72         var me = this;
73         this.type = type;
74         this.name = name;
75         this.value = value;
76     }
77
78     function connect(url, protocol, sucess_cb, error_cb) {
79         self.socketUrl = typeof url !== 'undefined' ? url : self.socketUrl;
80         self.socketProtocol = typeof protocol !== 'undefined' ? protocol : self.socketProtocol;
81         self.successCB = sucess_cb;
82         self.errorCB = error_cb;
83
84         if ('WebSocket' in window) {
85             if (self.socketProtocol.length > 0) {
86                 self.socket = new WebSocket(self.socketUrl, self.socketProtocol);
87             } else {
88                 self.socket = new WebSocket(self);
89             }
90
91             self.socket.onopen = function() {
92                 self.connected = true;
93                 console.log('socket opened');
94                 self.successCB();
95             };
96
97             self.socket.onclose = function() {
98                 self.connected = false;
99                 console.log('socket closed');
100
101                 if (dummyBackend) {
102                     /* fake the connection for now */
103                     self.connected = true;
104                     self.successCB();
105                 }
106             };
107
108             self.socket.onerror = function(e) {
109                 console.log('socket error: ', e.data);
110                 self.errorCB();
111             };
112
113             self.socket.onmessage = function(e) {
114                 self.receive(e.data);
115             };
116         } else {
117             console.log('Websockets not supported');
118         }
119     }
120
121     function send(msg, success_cb, error_cb) {
122         if (!this.connected) {
123             if (errorCB !== undefined) {
124                 errorCB('\"' + obj.name + '\" method failed because socket is closed');
125             }
126             return;
127         }
128         var i = this.methodIdx;
129         this.methodIdx = (this.methodIdx + 1) % 100;
130         this.methodCalls[i] = new this.MethodCall(msg.transactionid, msg.name, success_cb, error_cb);
131         this.methodCalls[i].start();
132         console.log('methodCalls[', i, '].start()');
133         if (dummyBackend) {
134             /* fake with dummy data */
135             dummyBackendSend(msg);
136         } else {
137             this.socket.send(JSON.stringify(msg));
138         }
139     }
140
141     function fireEvent(type, name, value) {
142         var event = new this.EventObject(type, name, value);
143         console.log('Fire event: [' + type + ', ' + name + ', ' + value + ']');
144         event_callbacks.fire(event);
145     }
146
147     function receive(msg) {
148         var self = this;
149         var event;
150         try {
151             event = JSON.parse(msg);
152         } catch (e) {
153             self.iErrorCB('GARBAGE MESSAGE: ' + msg);
154             return;
155         }
156
157         if ((event === undefined) || (event.type === undefined) || (event.name === undefined)) {
158             self.iErrorCB('BADLY FORMED MESSAGE: ' + msg);
159             return;
160         } else {
161             if (event.type === 'methodReply') {
162                 var calls = this.methodCalls;
163                 for (var i = 0; i < calls.length; i++) {
164                     var call = calls[i];
165                     if (call && (!call.done) && (call.transactionid === event.transactionid)) {
166                         call.finish();
167                         console.log('methodCalls[', i, '].finish()');
168                         if (event.error !== undefined) {
169                             call.errorCB(event.error);
170                         }
171                         if (event.data !== undefined && call.successCB !== undefined) {
172                             call.successCB(event.data);
173                         }
174                         return;
175                     }
176                 }
177             }
178             else if (event.type === 'event') {
179                  self.fireSevent(event.name, event.data);
180             }
181         }
182     }
183
184     function generateTransactionId() {
185         var i, val = [];
186         for (i = 0; i < 8; i++) {
187             var num = Math.floor((Math.random() + 1) * 65536);
188             val[i] = num.toString(16).substring(1);
189         }
190         var uuid = val[0] + val[1] + '-' + val[2] + '-' + val[3] + '-' + val[4] + '-' + val[5] + val[6] + val[7];
191         return uuid;
192     }
193
194     function sendRequest(request_type, request_name, request_args, success_cb, error_cb) {
195         var msg = {
196             'type': request_type,
197             'name': request_name,
198             'transactionid': generateTransactionId(),
199             'data': request_args
200         };
201
202         console.log('wsAPI.sendRequest(): ', request_type);
203         console.log('request name: ', request_name);
204         console.log('request args: ', request_args);
205         send(msg, success_cb, error_cb);
206     }
207     
208     function subscribeEvents(callback) {
209         event_callbacks.add(callback);
210     }
211     
212     function unsubscribeEvents(callback) {
213         event_callbacks.remove(callback);
214     }
215
216     /* this is dummy data for testing purposes */
217     function dummyBackendSend(msg) {
218         if (dummyBackend) {
219             console.log('Sending to dummy server');
220
221                     var calls = this.methodCalls;
222                     var replyMsg = null;
223                 
224                     for (var i = 0; i < calls.length; i++) {
225                         var call = calls[i];
226                         if (call && (!call.done) && (call.transactionid === msg.transactionid)) {
227                             call.finish();
228                             console.log('methodCalls[', i, '].finish()');
229                             if (msg.error !== undefined) {
230                                 call.errorCB(msg.error);
231                             }
232                             if (msg.data !== undefined && call.successCB && call.errorCB !== undefined) {
233                                 console.log('Send request to dummy backend: type - ', msg.type);
234                                 console.log('Send request to dummy backend: name - ', msg.name);
235                                 console.log('Send request to dummy backend: args - ', msg.data);
236                                 switch(msg.type)
237                                 {
238                                         case WS_REQUEST_TYPE.BLUETOOTH:
239                                         if (msg.name === 'enable') {
240                                             fireEvent(WS_EVENT_TYPE.BLUETOOTH, 'enabled', true);
241                                         } else if (msg.name === 'disable') {
242                                             fireEvent(WS_EVENT_TYPE.BLUETOOTH, 'enabled', false);
243                                         }
244                                         else {
245                                                 call.errorCB('Unsupported request name: ' + msg.name);
246                                                 return;
247                                         }
248                                                 break;
249                                         case WS_REQUEST_TYPE.WIFI:
250                                         case WS_REQUEST_TYPE.DISPLAY:
251                                         case WS_REQUEST_TYPE.SOUND:
252                                         case WS_REQUEST_TYPE.DATETIME:
253                                         case WS_REQUEST_TYPE.LOCALE:
254                                                 call.errorCB('Request not implemented');
255                                                 return;
256                                         default:
257                                         call.errorCB('Invalid request type: ' + msg.type);
258                                             return;
259                                 }
260                                 call.successCB(replyMsg);
261                             }
262                             return;
263                         }
264             }
265         }
266     }
267
268     return {
269         connect: connect,
270         sendRequest: sendRequest,
271         subscribeEvents: subscribeEvents
272     }
273 })();