nutella_framework 0.4.5 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +14 -15
  3. data/VERSION +1 -1
  4. data/framework_components/beacon-cloud-bot/README.md +27 -0
  5. data/framework_components/beacon-cloud-bot/beacon_cloud_bot.rb +154 -0
  6. data/framework_components/beacon-cloud-bot/nutella.json +6 -0
  7. data/framework_components/beacon-cloud-bot/startup +4 -0
  8. data/framework_components/beacon-cloud-interface/LICENSE +21 -0
  9. data/framework_components/beacon-cloud-interface/Readme.md +0 -0
  10. data/framework_components/beacon-cloud-interface/bower.json +29 -0
  11. data/framework_components/beacon-cloud-interface/bower_components/bower-mqttws/.bower.json +23 -0
  12. data/framework_components/beacon-cloud-interface/bower_components/bower-mqttws/bower.json +14 -0
  13. data/framework_components/beacon-cloud-interface/bower_components/bower-mqttws/mqttws31.js +2081 -0
  14. data/framework_components/beacon-cloud-interface/bower_components/bower-mqttws/readme.md +4 -0
  15. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/.bower.json +37 -0
  16. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/LICENSE +21 -0
  17. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/README.md +15 -0
  18. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/bower.json +28 -0
  19. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/examples/browser/mqtt_client_hello_world.html +23 -0
  20. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/examples/browser/nutella_hello_world.html +52 -0
  21. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/examples/node/mqtt_client_hello_world.js +14 -0
  22. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/examples/node/nutella_hello_world.js +38 -0
  23. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/nutella_lib.js +789 -0
  24. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/package.json +30 -0
  25. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/simple-js-mqtt-client.js +428 -0
  26. data/framework_components/beacon-cloud-interface/css/animation.css +17 -0
  27. data/framework_components/beacon-cloud-interface/css/cursor.css +16 -0
  28. data/framework_components/beacon-cloud-interface/css/page_layout.css +73 -0
  29. data/framework_components/beacon-cloud-interface/index.html +157 -0
  30. data/framework_components/beacon-cloud-interface/js/lib/nutella_lib.js +4039 -0
  31. data/framework_components/beacon-cloud-interface/js/react/beacon-add.js +102 -0
  32. data/framework_components/beacon-cloud-interface/js/react/beacon-table.js +73 -0
  33. data/framework_components/beacon-cloud-interface/js/react/beacon.js +97 -0
  34. data/framework_components/beacon-cloud-interface/nutella.json +6 -0
  35. data/framework_components/example_framework_web_interface/index.html +11 -2
  36. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/.npmignore +10 -0
  37. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/.travis.yml +5 -0
  38. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/LICENSE +21 -0
  39. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/README.md +27 -0
  40. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/dist/nutella_lib.js +4039 -0
  41. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/dist/nutella_lib.js.map +1 -0
  42. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/examples/browser_hello_world.html +67 -0
  43. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/examples/node_hello_world.js +51 -0
  44. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/gulpfile.js +31 -0
  45. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/package.json +41 -0
  46. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/app_core.js +19 -0
  47. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/app_core_browser.js +17 -0
  48. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/app_log.js +50 -0
  49. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/app_net.js +279 -0
  50. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/app_persist.js +20 -0
  51. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/fr_core_browser.js +17 -0
  52. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/fr_log.js +50 -0
  53. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/fr_net.js +499 -0
  54. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/nutella_i.js +74 -0
  55. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/nutella_i_browser.js +130 -0
  56. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/nutella_lib.js +91 -0
  57. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/nutella_lib_browser.js +90 -0
  58. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/run_log.js +51 -0
  59. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/run_net.js +84 -0
  60. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/run_persist.js +20 -0
  61. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/util/net.js +327 -0
  62. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/test/nutella.test.js +16 -0
  63. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/test/runner.html +22 -0
  64. data/framework_components/example_framework_web_interface/package.json +15 -0
  65. data/framework_components/{order.json.example → order.json} +0 -0
  66. data/framework_components/runs_list_bot/{app_runs_list_bot.rb → runs_list_bot.rb} +9 -3
  67. data/framework_components/runs_list_bot/startup +1 -1
  68. data/lib/commands/meta/run_command.rb +21 -36
  69. data/lib/commands/start.rb +9 -199
  70. data/lib/commands/util/components_list.rb +68 -0
  71. data/lib/commands/util/components_starter.rb +169 -0
  72. data/nutella_framework.gemspec +109 -47
  73. data/nutella_lib/framework_net.rb +17 -13
  74. metadata +84 -106
@@ -0,0 +1,789 @@
1
+ /******************
2
+ * nutella_lib.js *
3
+ ******************/
4
+
5
+ (function() {
6
+ "use strict";
7
+
8
+ // Establish the root object, `window` in the browser, or `exports` on the server.
9
+ var root = this;
10
+
11
+ // Save the previous value of the `nutella` variable for noConflict().
12
+ var previousNutella = root.NUTELLA;
13
+
14
+ // Internal reference to this library (used below)
15
+ var nutella = {};
16
+
17
+ // Detect if we are in the browser or in node and
18
+ // load the appropriate dependencies
19
+ var isNode;
20
+ var mqtt_lib;
21
+ if (typeof window === 'undefined') {
22
+ isNode = true; // Node
23
+ mqtt_lib = require('mqtt');
24
+ if( typeof mqtt_lib === 'undefined' )
25
+ throw new Error('This MQTT client requires the mqtt library (https://www.npmjs.com/package/mqtt)');
26
+ } else {
27
+ isNode = false; // Browser
28
+ mqtt_lib = root.Paho.MQTT;
29
+ if( typeof mqtt_lib === 'undefined' )
30
+ throw new Error('This MQTT client requires the mqtt-ws library (https://github.com/M2MConnections/mqtt-ws) a wrapper of Paho.js');
31
+
32
+ }
33
+
34
+
35
+
36
+ /**
37
+ * Runs nutella.js in noConflict mode by
38
+ * returning the NUTELLA variable to its previous owner.
39
+ *
40
+ * @return a reference to the nutella object defined by this library.
41
+ */
42
+ nutella.noConflict = function() {
43
+ root.NUTELLA = previousNutella;
44
+ return nutella;
45
+ };
46
+
47
+
48
+
49
+ /**
50
+ * Parses URL parameters.
51
+ * Note: this function is not defined if we are not in the browser.
52
+ *
53
+ * @return {Object} An object containing all the URL query parameters
54
+ */
55
+ if (!isNode) {
56
+ nutella.parseURLParameters = function () {
57
+ var str = location.search;
58
+ var queries = str.replace(/^\?/, '').split('&');
59
+ var searchObject = {};
60
+ for( var i = 0; i < queries.length; i++ ) {
61
+ var split = queries[i].split('=');
62
+ searchObject[split[0]] = split[1];
63
+ }
64
+ return searchObject;
65
+ }
66
+ }
67
+
68
+
69
+ /**
70
+ * Creates a new instance of nutella
71
+ * and initialize it
72
+ * This is a factory method.
73
+ *
74
+ *
75
+ * @param {string} run_id - the run_id this component is launched in
76
+ * @param {string} broker_hostname - the hostname of the broker.
77
+ * @param {string} component_id - the name of this component
78
+ */
79
+ nutella.init = function(run_id, broker_hostname, component_id) {
80
+ if (run_id===undefined || broker_hostname===undefined || component_id=== undefined) {
81
+ console.warn("Couldn't initialize nutella. Make sure you are setting all three the required parameters (runId, broker_hostname, componentId'");
82
+ }
83
+ return new NutellaInstance(run_id, broker_hostname, component_id);
84
+ };
85
+
86
+
87
+
88
+ /**
89
+ * Defines a nutella instance.
90
+ *
91
+ * @param {string} run_id - the run_id this component is launched in
92
+ * @param {string} broker_hostname - the hostname of the broker.
93
+ * @param {string} component_id - the name of this component
94
+ */
95
+ var NutellaInstance = function (run_id, broker_hostname, component_id) {
96
+
97
+ this.mqtt_client = new SimpleMQTTClient(broker_hostname);
98
+ this.runId = run_id;
99
+ this.componentId = component_id;
100
+
101
+ // Initialized the various sub-modules
102
+ this.net = new NetSubModule(this);
103
+ this.persist = new PersistSubModule(this);
104
+ // ... other sub-modules here
105
+ };
106
+
107
+
108
+
109
+ /**
110
+ * Sets the resource id for this instance of nutella
111
+ *
112
+ * @param {string} resource_id - the resource_id associated to this instance of nutella
113
+ */
114
+ NutellaInstance.prototype.setResourceId = function(resource_id){
115
+ this.resourceId = resource_id;
116
+ };
117
+
118
+
119
+
120
+
121
+ //
122
+ // END OF: main nutella protocol module
123
+ //
124
+
125
+
126
+
127
+ // --------------------------------------------------------------------------------------------
128
+ // net sub-module
129
+ // --------------------------------------------------------------------------------------------
130
+
131
+
132
+ var NetSubModule = function(main_nutella) {
133
+ // Store a reference to the main module
134
+ this.main_nutella = main_nutella;
135
+
136
+ // Store the subscriptions and the relative callbacks
137
+ this.subscriptions = [];
138
+ this.callbacks = [];
139
+ };
140
+
141
+
142
+
143
+ /**
144
+ * Subscribes to a channel or filter.
145
+ *
146
+ * @param channel
147
+ * @param callback
148
+ * @param done_callback
149
+ */
150
+ NetSubModule.prototype.subscribe = function(channel, callback, done_callback) {
151
+ // Prevent multiple subscriptions to the same channel
152
+ if (this.subscriptions.indexOf(channel)!==-1)
153
+ throw new Error('You can`t subscribe twice to the same channel');
154
+ // Pad the channel
155
+ var runId = this.main_nutella.runId;
156
+ var new_channel = runId + '/' + channel;
157
+ // Define callbacks
158
+ var mqtt_cb;
159
+ if (isChannelWildcard(channel))
160
+ mqtt_cb = function(mqtt_message, mqtt_channel) {
161
+ // Ignore anything that is not JSON or
162
+ // doesn't comply to the nutella protocol
163
+ try {
164
+ var f = extractFieldsFromMessage(mqtt_message);
165
+ var clean_channel = mqtt_channel.replace(runId+'/', '');
166
+ if (f.type==='publish')
167
+ callback(f.payload, clean_channel, f.componentId, f.resourceId);
168
+ } catch(err) {
169
+ }
170
+ };
171
+ else
172
+ mqtt_cb = function(mqtt_message) {
173
+ // Ignore anything that is not JSON or
174
+ // doesn't comply to the nutella protocol
175
+ try {
176
+ var f = extractFieldsFromMessage(mqtt_message);
177
+ if (f.type==='publish')
178
+ callback(f.payload, f.componentId, f.resourceId);
179
+ } catch(err) {
180
+ }
181
+ };
182
+ // Update subscriptions, callbacks and subscribe
183
+ this.subscriptions.push(channel);
184
+ this.callbacks.push(mqtt_cb);
185
+ this.main_nutella.mqtt_client.subscribe(new_channel, mqtt_cb, done_callback);
186
+ };
187
+
188
+
189
+
190
+ /**
191
+ * Unsubscribes from a channel
192
+ *
193
+ * @param channel
194
+ * @param done_callback
195
+ */
196
+ NetSubModule.prototype.unsubscribe = function(channel, done_callback) {
197
+ // Find index of subscription and retrieve relative callback
198
+ var idx = this.subscriptions.indexOf(channel);
199
+ var cbAtIdx = this.callbacks[idx];
200
+ // Pad the channel
201
+ var new_channel = this.main_nutella.runId + '/' + channel;
202
+ // Unsubscribe
203
+ this.subscriptions.splice(idx, 1);
204
+ this.callbacks.splice(idx, 1);
205
+ this.main_nutella.mqtt_client.unsubscribe(new_channel, cbAtIdx, done_callback);
206
+ };
207
+
208
+
209
+
210
+ /**
211
+ * Publishes a message to a channel
212
+ *
213
+ * @param channel
214
+ * @param message
215
+ */
216
+ NetSubModule.prototype.publish = function(channel, message) {
217
+ // Pad the channel
218
+ var new_channel = this.main_nutella.runId + '/' + channel;
219
+ var m = prepareMessageForPublish(message, this.main_nutella.componentId, this.main_nutella.resourceId);
220
+ this.main_nutella.mqtt_client.publish(new_channel, m);
221
+ };
222
+
223
+
224
+
225
+ /**
226
+ * Sends a request.
227
+ *
228
+ * @param channel
229
+ * @param message
230
+ * @param callback
231
+ * @param done_callback
232
+ */
233
+ NetSubModule.prototype.request = function(channel, message, callback, done_callback) {
234
+ // Handle optional message parameter
235
+ if (typeof message==='function') {
236
+ if (callback!==undefined)
237
+ done_callback = callback;
238
+ callback = message;
239
+ message = undefined;
240
+ }
241
+ // Pad channel
242
+ var new_channel = this.main_nutella.runId + '/' + channel;
243
+ // Prepare request
244
+ var request_id = Math.floor((Math.random() * 100000) + 1).toString();
245
+ var mqtt_request = prepareRequest(message, request_id, this.main_nutella.componentId, this.main_nutella.resourceId);
246
+ // Prepare callback to handle response
247
+ var mqtt_cb = function(mqtt_response) {
248
+ // Ignore anything that is not JSON or
249
+ // doesn't comply to the nutella protocol
250
+ try {
251
+ var f = extractFieldsFromMessage(mqtt_response);
252
+ var response_id = extractIdFromMessage(mqtt_response);
253
+ // Only handle responses that have proper id set
254
+ if (f.type==='response' && response_id===request_id) {
255
+ // Execute callback
256
+ callback(f.payload);
257
+ }
258
+ } catch(err) {
259
+ }
260
+ };
261
+ // Subscribe to response
262
+ var mqtt_cli = this.main_nutella.mqtt_client;
263
+ mqtt_cli.subscribe(new_channel, mqtt_cb, function() {
264
+ // Once we are subscribed we publish the request
265
+ mqtt_cli.publish(new_channel, mqtt_request);
266
+ // Execute optional done callback
267
+ if (done_callback!==undefined) done_callback();
268
+ });
269
+ };
270
+
271
+
272
+
273
+ /**
274
+ * Handles requests.
275
+ *
276
+ * @param channel
277
+ * @param callback
278
+ * @param done_callback
279
+ */
280
+ NetSubModule.prototype.handle_requests = function(channel, callback, done_callback) {
281
+ // Pad the channel
282
+ var new_channel = this.main_nutella.runId + '/' + channel;
283
+ // Prepare callback
284
+ var c_id = this.main_nutella.componentId;
285
+ var r_id = this.main_nutella.resourceId;
286
+ var mqtt_cli = this.main_nutella.mqtt_client;
287
+ var mqtt_cb = function(mqtt_request) {
288
+ // Ignore anything that is not JSON or
289
+ // doesn't comply to the nutella protocol
290
+ try {
291
+ var f = extractFieldsFromMessage(mqtt_request);
292
+ var id = extractIdFromMessage(mqtt_request);
293
+ } catch(err) {
294
+ return;
295
+ }
296
+ // Only handle requests that have proper id set
297
+ if (f.type!=='request' || id===undefined) return;
298
+ // Execute callback, assemble the response and publish
299
+ var res_json = callback(f.payload, f.componentId, f.resourceId);
300
+ var mqtt_response = prepareResponse(res_json, id, c_id, r_id);
301
+ mqtt_cli.publish(new_channel, mqtt_response);
302
+ };
303
+ // Subscribe
304
+ this.main_nutella.mqtt_client.subscribe(new_channel, mqtt_cb, done_callback);
305
+ };
306
+
307
+
308
+
309
+ //
310
+ // Helper function
311
+ // Extracts nutella parameters from a received message
312
+ //
313
+ function extractFieldsFromMessage(message) {
314
+ var params = JSON.parse(message);
315
+ var s = params.from.split('/');
316
+ delete params.from;
317
+ params.componentId = s[0];
318
+ if (s.length===2)
319
+ params.resourceId = s[1];
320
+ return params;
321
+ }
322
+
323
+
324
+
325
+ //
326
+ // Helper function
327
+ // Extracts request id from a received message
328
+ //
329
+ function extractIdFromMessage(message) {
330
+ var params = JSON.parse(message);
331
+ return params.id;
332
+ }
333
+
334
+
335
+
336
+ //
337
+ // Helper function
338
+ // Pads a message with the nutella protocol fields
339
+ //
340
+ function prepareMessageForPublish(message, componentId, resourceId) {
341
+ var from = resourceId===undefined ? componentId : (componentId + '/' + resourceId);
342
+ if (message===undefined)
343
+ return JSON.stringify({type: 'publish', from: from});
344
+ return JSON.stringify({type: 'publish', from: from, payload: message});
345
+ }
346
+
347
+
348
+
349
+ //
350
+ // Helper function
351
+ // Pads a request with the nutella protocol fields
352
+ //
353
+ function prepareRequest(message, request_id, componentId, resourceId) {
354
+ var from = resourceId===undefined ? componentId : (componentId + '/' + resourceId);
355
+ if (message===undefined)
356
+ return JSON.stringify({id: request_id, type: 'request', from: from});
357
+ return JSON.stringify({id: request_id, type: 'request', from: from, payload: message});
358
+ }
359
+
360
+
361
+
362
+ //
363
+ // Helper function
364
+ // Pads a response with the nutella protocol fields
365
+ //
366
+ function prepareResponse(message, request_id, componentId, resourceId) {
367
+ var from = resourceId===undefined ? componentId : (componentId + '/' + resourceId);
368
+ if (message===undefined)
369
+ return JSON.stringify({id: request_id, type: 'response', from: from});
370
+ return JSON.stringify({id: request_id, type: 'response', from: from, payload: message});
371
+ }
372
+
373
+
374
+
375
+ // Helper function to test if a channel is wildcard or not.
376
+ // Returns true if it is Returns true is.
377
+ // See MQTT specification for wildcard channels
378
+ // {http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106 here
379
+ //
380
+ function isChannelWildcard(channel) {
381
+ return channel.indexOf('#')!==-1 || channel.indexOf('+')!==-1;
382
+ }
383
+
384
+
385
+ //
386
+ // END OF: net sub-module
387
+ //
388
+
389
+
390
+
391
+ // --------------------------------------------------------------------------------------------
392
+ // persist sub-module
393
+ // --------------------------------------------------------------------------------------------
394
+
395
+
396
+ var PersistSubModule = function(main_nutella) {
397
+ // Store a reference to the main module
398
+ this.main_nutella = main_nutella;
399
+ };
400
+
401
+
402
+
403
+ PersistSubModule.prototype.persist = function () {
404
+ console.log("This is just a test method for the persist sub-module");
405
+ };
406
+
407
+
408
+
409
+
410
+ //
411
+ // END OF: persist sub-module
412
+ //
413
+
414
+
415
+
416
+
417
+ // --------------------------------------------------------------------------------------------
418
+ // Simple MQTT client
419
+ // --------------------------------------------------------------------------------------------
420
+
421
+
422
+ /**
423
+ * Defines a Simple MQTT client.
424
+ *
425
+ * @param {string} host - the hostname of the broker.
426
+ * @param {string} [clientId] - the unique name of this client. If no ID is provided a random one is generated
427
+ */
428
+ var SimpleMQTTClient = function (host, clientId) {
429
+
430
+ // Initializes the object that stores subscriptions
431
+ this.subscriptions = {};
432
+ // Initializes the object that holds the internal client
433
+ this.client = {};
434
+ // Functions backlog
435
+ this.backlog = [];
436
+
437
+ // Handles the optional clientId parameter
438
+ if (arguments.length === 1 || clientId === undefined) {
439
+ clientId = generateRandomClientId();
440
+ }
441
+
442
+ // Connect
443
+ if (isNode)
444
+ this.client = connectNode(this.subscriptions, host, clientId);
445
+ else
446
+ this.client = connectBrowser(this.subscriptions, this.backlog, host, clientId);
447
+ };
448
+
449
+
450
+ //
451
+ // Helper function that connects the MQTT client in node
452
+ //
453
+ function connectNode (subscriptions, host, clientId) {
454
+ // Create client
455
+ var url = "tcp://" + host + ":1883";
456
+ var client = mqtt_lib.connect(url, {clientId : clientId});
457
+ // Register incoming message callback
458
+ client.on('message', function(channel, message) {
459
+ // Executes the appropriate channel callback
460
+ var cbs = findCallbacks(subscriptions, channel);
461
+ if (cbs!==undefined) {
462
+ if (Object.keys(subscriptions).indexOf(channel)!==-1)
463
+ cbs.forEach(function(cb) {
464
+ cb(message);
465
+ });
466
+ else
467
+ cbs.forEach(function(cb) {
468
+ cb(message, channel);
469
+ });
470
+
471
+ }
472
+ });
473
+ return client;
474
+ }
475
+
476
+
477
+ //
478
+ // Helper function that connects the MQTT client in the browser
479
+ //
480
+ function connectBrowser (subscriptions, backlog, host, clientId) {
481
+ // Create client
482
+ var client = new mqtt_lib.Client(host, Number(1884), clientId);
483
+ // Register callback for connection lost
484
+ client.onConnectionLost = function() {
485
+ // TODO try to reconnect
486
+ };
487
+ // Register callback for received message
488
+ client.onMessageArrived = function (message) {
489
+ // Executes the appropriate channel callback
490
+ var cbs = findCallbacks(subscriptions, message.destinationName);
491
+ if (cbs!==undefined) {
492
+ if (Object.keys(subscriptions).indexOf(message.destinationName)!==-1)
493
+ cbs.forEach(function(cb) {
494
+ cb(message.payloadString);
495
+ });
496
+ else
497
+ cbs.forEach(function(cb) {
498
+ cb(message.payloadString, message.destinationName);
499
+ });
500
+ }
501
+ };
502
+ // Connect
503
+ client.connect({onSuccess: function() {
504
+ // Execute the backlog of operations performed while the client wasn't connected
505
+ backlog.forEach(function(e) {
506
+ e.op.apply(this, e.params);
507
+ });
508
+ }});
509
+ return client;
510
+ }
511
+
512
+
513
+
514
+ /**
515
+ * Disconnects from the MQTT client.
516
+ */
517
+ SimpleMQTTClient.prototype.disconnect = function () {
518
+ if (isNode)
519
+ this.client.end();
520
+ else
521
+ this.client.disconnect();
522
+ this.subscriptions = {};
523
+ };
524
+
525
+
526
+
527
+ /**
528
+ * Subscribes to a channel and registers a callback.
529
+ *
530
+ * @param {string} channel - the channel we are subscribing to.
531
+ * @param {callback} callback - A function that is executed every time a message is received on that channel.
532
+ * @param {callback} [done_callback] - A function that is executed once the subscribe operation has completed successfully.
533
+ */
534
+ SimpleMQTTClient.prototype.subscribe = function (channel, callback, done_callback) {
535
+ // Subscribe
536
+ if( isNode )
537
+ subscribeNode(this.client, this.subscriptions, channel, callback, done_callback);
538
+ else
539
+ subscribeBrowser(this.client, this.subscriptions, this.backlog, channel, callback, done_callback);
540
+ };
541
+
542
+
543
+ //
544
+ // Helper function that subscribes to a channel in node
545
+ //
546
+ function subscribeNode (client, subscriptions, channel, callback, done_callback) {
547
+ if (subscriptions[channel]===undefined) {
548
+ subscriptions[channel] = [callback];
549
+ client.subscribe(channel, {qos: 0}, function() {
550
+ // If there is a done_callback defined, execute it
551
+ if (done_callback!==undefined) done_callback();
552
+ });
553
+ } else {
554
+ subscriptions[channel].push(callback);
555
+ }
556
+ }
557
+
558
+
559
+ //
560
+ // Helper function that subscribes to a channel in the browser
561
+ //
562
+ function subscribeBrowser (client, subscriptions, backlog, channel, callback, done_callback) {
563
+ if ( addToBacklog(client, backlog, subscribeBrowser, [client, subscriptions, backlog, channel, callback, done_callback]) ) return;
564
+ if (subscriptions[channel]===undefined) {
565
+ subscriptions[channel] = [callback];
566
+ client.subscribe(channel, {qos: 0, onSuccess: function() {
567
+ // If there is a done_callback defined, execute it
568
+ if (done_callback!==undefined) done_callback();
569
+ }});
570
+ } else {
571
+ subscriptions[channel].push(callback);
572
+ // If there is a done_callback defined, execute it
573
+ if (done_callback!==undefined) done_callback();
574
+ }
575
+ }
576
+
577
+
578
+
579
+ /**
580
+ * Unsubscribe from a channel.
581
+ *
582
+ * @param {string} channel - the channel we are un-subscribing from.
583
+ * @param {function} callback - the callback we are trying to de-register
584
+ * @param {callback} [done_callback] - A function that is executed once the unsubscribe operation has completed successfully.
585
+ */
586
+ SimpleMQTTClient.prototype.unsubscribe = function (channel, callback, done_callback) {
587
+ if( isNode )
588
+ unsubscribeNode(this.client, this.subscriptions, channel, callback, done_callback);
589
+ else
590
+ unsubscribeBrowser(this.client, this.subscriptions, this.backlog, channel, callback, done_callback);
591
+ };
592
+
593
+
594
+ //
595
+ // Helper function that unsubscribes from a channel in node
596
+ //
597
+ var unsubscribeNode = function(client, subscriptions, channel, callback, done_callback) {
598
+ if (subscriptions[channel]===undefined)
599
+ return;
600
+ subscriptions[channel].splice(subscriptions[channel].indexOf(callback), 1);
601
+ if (subscriptions[channel].length===0) {
602
+ delete subscriptions[channel];
603
+ client.unsubscribe(channel, function() {
604
+ // If there is a done_callback defined, execute it
605
+ if (done_callback!==undefined) done_callback();
606
+ });
607
+ }
608
+ };
609
+
610
+
611
+ //
612
+ // Helper function that unsubscribes from a channel in the browser
613
+ //
614
+ var unsubscribeBrowser = function(client, subscriptions, backlog, channel, callback, done_callback) {
615
+ if ( addToBacklog(client, backlog, unsubscribeBrowser, [client, subscriptions, backlog, channel, callback, done_callback]) ) return;
616
+ if (subscriptions[channel]===undefined)
617
+ return;
618
+ subscriptions[channel].splice(subscriptions[channel].indexOf(callback), 1);
619
+ if (subscriptions[channel].length===0) {
620
+ delete subscriptions[channel];
621
+ client.unsubscribe(channel, {onSuccess : function() {
622
+ // If there is a done_callback defined, execute it
623
+ if (done_callback!==undefined) done_callback();
624
+ }});
625
+ }
626
+ };
627
+
628
+
629
+ /**
630
+ * Lists all the channels we are currently subscribed to.
631
+ *
632
+ * @returns {Array} a lists of all the channels we are currently subscribed to.
633
+ */
634
+ SimpleMQTTClient.prototype.getSubscriptions = function () {
635
+ return Object.keys(this.subscriptions);
636
+ };
637
+
638
+
639
+ /**
640
+ * Publishes a message to a channel.
641
+ *
642
+ * @param {string} channel - the channel we are publishing to.
643
+ * @param {string} message - the message we are publishing.
644
+ */
645
+ SimpleMQTTClient.prototype.publish = function (channel, message) {
646
+ if (isNode)
647
+ publishNode(this.client, channel, message);
648
+ else
649
+ publishBrowser(this.client, this.backlog, channel, message)
650
+ };
651
+
652
+
653
+ //
654
+ // Helper function that publishes to a channel in node
655
+ //
656
+ var publishNode = function (client, channel, message) {
657
+ client.publish(channel, message);
658
+ };
659
+
660
+
661
+ //
662
+ // Helper function that publishes to a channel in the browser
663
+ //
664
+ var publishBrowser = function (client, backlog, channel, message) {
665
+ if ( addToBacklog(client, backlog, publishBrowser, [client, backlog, channel, message]) ) return;
666
+ message = new mqtt_lib.Message(message);
667
+ message.destinationName = channel;
668
+ client.send(message);
669
+ };
670
+
671
+
672
+ //
673
+ // Helper functions for Simple MQTT client
674
+ //
675
+
676
+
677
+ //
678
+ // Helper function that generates a random client ID
679
+ //
680
+ function generateRandomClientId () {
681
+ var length = 22;
682
+ var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
683
+ var result = '';
684
+ for (var i = length; i > 0; --i) {
685
+ result += chars[Math.round(Math.random() * (chars.length - 1))];
686
+ }
687
+ return result;
688
+ };
689
+
690
+
691
+ //
692
+ // Helper function that selects the right callback when a message is received
693
+ //
694
+ function findCallbacks (subscriptions, channel) {
695
+ // First try to see if a callback for the exact channel exists
696
+ if(Object.keys(subscriptions).indexOf(channel)!==-1)
697
+ return subscriptions[channel];
698
+ // If it doesn't then let's try to see if the channel matches a wildcard callback
699
+ var pattern = matchesWildcard(subscriptions, channel);
700
+ if (pattern!== undefined) {
701
+ return subscriptions[pattern];
702
+ }
703
+ // If there's no exact match or wildcard we have to return undefined
704
+ return undefined;
705
+ };
706
+
707
+
708
+ //
709
+ // Helper function that tries to match a channel with each subscription
710
+ // it returns undefined if no match is found
711
+ //
712
+ function matchesWildcard (subscriptions, channel) {
713
+ var i;
714
+ var subs = Object.keys(subscriptions);
715
+ for (i=0; i < subs.length; i++) {
716
+ if (matchesFilter(subs[i], channel)) {
717
+ return subs[i];
718
+ }
719
+ }
720
+ return undefined;
721
+ };
722
+
723
+
724
+ //
725
+ // Helper function that checks a certain channel and see if it matches a wildcard pattern
726
+ // Returns true if the channel matches a pattern (including the exact pattern)
727
+ //
728
+ function matchesFilter (pattern, channel) {
729
+ // If multi-level wildcard is the only character in pattern, then any string will match
730
+ if (pattern==="#") {
731
+ return true;
732
+ }
733
+ // Handle all other multi-level wildcards
734
+ // FROM SPEC: The number sign (‘#’ U+0023) is a wildcard character that matches any number of levels within a topic. The multi-level wildcard represents the parent and any number of child levels. The multi-level wildcard character MUST be specified either on its own or following a topic level separator. In either case it MUST be the last character specified in the Topic Filter
735
+ var p_wo_wildcard = pattern.substring(0, pattern.length-2);
736
+ var str_wo_details = channel.substring(0, pattern.length-2);
737
+ if (pattern.slice(-1)=='#' && p_wo_wildcard==str_wo_details) {
738
+ return true;
739
+ }
740
+ // TODO Handle single-level wildcards (+)
741
+ // FROM SPEC: The single-level wildcard can be used at any level in the Topic Filter, including first and last levels. Where it is used it MUST occupy an entire level of the filter [MQTT-4.7.1-3]. It can be used at more than one level in the Topic Filter and can be used in conjunction with the multilevel wildcard.
742
+ // http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718107
743
+ return false;
744
+ };
745
+
746
+
747
+ //
748
+ // Helper method that queues operations into the backlog.
749
+ // This method is used to make `connect` "synchronous" by
750
+ // queueing up operations on the client until it is connected.
751
+ //
752
+ // @param {string} method - the method that needs to be added to the backlog
753
+ // @param {Array} parameters - parameters to the method being added to the backlog
754
+ // @returns {boolean} true if the method was successfully added, false otherwise
755
+ //
756
+ function addToBacklog (client, backlog, method, parameters) {
757
+ if (!client.isConnected() ) {
758
+ backlog.push({
759
+ op : method,
760
+ params : parameters
761
+ });
762
+ return true;
763
+ }
764
+ return false;
765
+ };
766
+
767
+
768
+
769
+
770
+ //
771
+ // End of methods definition for SimpleMQTTClient
772
+ //
773
+
774
+
775
+ // Export the nutella object
776
+ // For node.js, also with backwards-compatibility for the old `require()` API.
777
+ // If we're in the browser, add `NUTELLA` as a global object.
778
+ if (typeof exports !== 'undefined') {
779
+ if (typeof module !== 'undefined' && module.exports) {
780
+ exports = module.exports = nutella;
781
+ }
782
+ exports.NUTELLA = nutella;
783
+ } else {
784
+ root.NUTELLA = nutella;
785
+ }
786
+
787
+
788
+
789
+ }.call(this));