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,2081 @@
1
+ /*******************************************************************************
2
+ * Copyright (c) 2013 IBM Corp.
3
+ *
4
+ * All rights reserved. This program and the accompanying materials
5
+ * are made available under the terms of the Eclipse Public License v1.0
6
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
7
+ *
8
+ * The Eclipse Public License is available at
9
+ * http://www.eclipse.org/legal/epl-v10.html
10
+ * and the Eclipse Distribution License is available at
11
+ * http://www.eclipse.org/org/documents/edl-v10.php.
12
+ *
13
+ * Contributors:
14
+ * Andrew Banks - initial API and implementation and initial documentation
15
+ *******************************************************************************/
16
+
17
+
18
+ // Only expose a single object name in the global namespace.
19
+ // Everything must go through this module. Global Paho.MQTT module
20
+ // only has a single public function, client, which returns
21
+ // a Paho.MQTT client object given connection details.
22
+
23
+ /**
24
+ * Send and receive messages using web browsers.
25
+ * <p>
26
+ * This programming interface lets a JavaScript client application use the MQTT V3.1 or
27
+ * V3.1.1 protocol to connect to an MQTT-supporting messaging server.
28
+ *
29
+ * The function supported includes:
30
+ * <ol>
31
+ * <li>Connecting to and disconnecting from a server. The server is identified by its host name and port number.
32
+ * <li>Specifying options that relate to the communications link with the server,
33
+ * for example the frequency of keep-alive heartbeats, and whether SSL/TLS is required.
34
+ * <li>Subscribing to and receiving messages from MQTT Topics.
35
+ * <li>Publishing messages to MQTT Topics.
36
+ * </ol>
37
+ * <p>
38
+ * The API consists of two main objects:
39
+ * <dl>
40
+ * <dt><b>{@link Paho.MQTT.Client}</b></dt>
41
+ * <dd>This contains methods that provide the functionality of the API,
42
+ * including provision of callbacks that notify the application when a message
43
+ * arrives from or is delivered to the messaging server,
44
+ * or when the status of its connection to the messaging server changes.</dd>
45
+ * <dt><b>{@link Paho.MQTT.Message}</b></dt>
46
+ * <dd>This encapsulates the payload of the message along with various attributes
47
+ * associated with its delivery, in particular the destination to which it has
48
+ * been (or is about to be) sent.</dd>
49
+ * </dl>
50
+ * <p>
51
+ * The programming interface validates parameters passed to it, and will throw
52
+ * an Error containing an error message intended for developer use, if it detects
53
+ * an error with any parameter.
54
+ * <p>
55
+ * Example:
56
+ *
57
+ * <code><pre>
58
+ client = new Paho.MQTT.Client(location.hostname, Number(location.port), "clientId");
59
+ client.onConnectionLost = onConnectionLost;
60
+ client.onMessageArrived = onMessageArrived;
61
+ client.connect({onSuccess:onConnect});
62
+
63
+ function onConnect() {
64
+ // Once a connection has been made, make a subscription and send a message.
65
+ console.log("onConnect");
66
+ client.subscribe("/World");
67
+ message = new Paho.MQTT.Message("Hello");
68
+ message.destinationName = "/World";
69
+ client.send(message);
70
+ };
71
+ function onConnectionLost(responseObject) {
72
+ if (responseObject.errorCode !== 0)
73
+ console.log("onConnectionLost:"+responseObject.errorMessage);
74
+ };
75
+ function onMessageArrived(message) {
76
+ console.log("onMessageArrived:"+message.payloadString);
77
+ client.disconnect();
78
+ };
79
+ * </pre></code>
80
+ * @namespace Paho.MQTT
81
+ */
82
+
83
+ if (typeof Paho === "undefined") {
84
+ Paho = {};
85
+ }
86
+
87
+ Paho.MQTT = (function (global) {
88
+
89
+ // Private variables below, these are only visible inside the function closure
90
+ // which is used to define the module.
91
+
92
+ var version = "@VERSION@";
93
+ var buildLevel = "@BUILDLEVEL@";
94
+
95
+ /**
96
+ * Unique message type identifiers, with associated
97
+ * associated integer values.
98
+ * @private
99
+ */
100
+ var MESSAGE_TYPE = {
101
+ CONNECT: 1,
102
+ CONNACK: 2,
103
+ PUBLISH: 3,
104
+ PUBACK: 4,
105
+ PUBREC: 5,
106
+ PUBREL: 6,
107
+ PUBCOMP: 7,
108
+ SUBSCRIBE: 8,
109
+ SUBACK: 9,
110
+ UNSUBSCRIBE: 10,
111
+ UNSUBACK: 11,
112
+ PINGREQ: 12,
113
+ PINGRESP: 13,
114
+ DISCONNECT: 14
115
+ };
116
+
117
+ // Collection of utility methods used to simplify module code
118
+ // and promote the DRY pattern.
119
+
120
+ /**
121
+ * Validate an object's parameter names to ensure they
122
+ * match a list of expected variables name for this option
123
+ * type. Used to ensure option object passed into the API don't
124
+ * contain erroneous parameters.
125
+ * @param {Object} obj - User options object
126
+ * @param {Object} keys - valid keys and types that may exist in obj.
127
+ * @throws {Error} Invalid option parameter found.
128
+ * @private
129
+ */
130
+ var validate = function(obj, keys) {
131
+ for (var key in obj) {
132
+ if (obj.hasOwnProperty(key)) {
133
+ if (keys.hasOwnProperty(key)) {
134
+ if (typeof obj[key] !== keys[key])
135
+ throw new Error(format(ERROR.INVALID_TYPE, [typeof obj[key], key]));
136
+ } else {
137
+ var errorStr = "Unknown property, " + key + ". Valid properties are:";
138
+ for (var key in keys)
139
+ if (keys.hasOwnProperty(key))
140
+ errorStr = errorStr+" "+key;
141
+ throw new Error(errorStr);
142
+ }
143
+ }
144
+ }
145
+ };
146
+
147
+ /**
148
+ * Return a new function which runs the user function bound
149
+ * to a fixed scope.
150
+ * @param {function} User function
151
+ * @param {object} Function scope
152
+ * @return {function} User function bound to another scope
153
+ * @private
154
+ */
155
+ var scope = function (f, scope) {
156
+ return function () {
157
+ return f.apply(scope, arguments);
158
+ };
159
+ };
160
+
161
+ /**
162
+ * Unique message type identifiers, with associated
163
+ * associated integer values.
164
+ * @private
165
+ */
166
+ var ERROR = {
167
+ OK: {code:0, text:"AMQJSC0000I OK."},
168
+ CONNECT_TIMEOUT: {code:1, text:"AMQJSC0001E Connect timed out."},
169
+ SUBSCRIBE_TIMEOUT: {code:2, text:"AMQJS0002E Subscribe timed out."},
170
+ UNSUBSCRIBE_TIMEOUT: {code:3, text:"AMQJS0003E Unsubscribe timed out."},
171
+ PING_TIMEOUT: {code:4, text:"AMQJS0004E Ping timed out."},
172
+ INTERNAL_ERROR: {code:5, text:"AMQJS0005E Internal error."},
173
+ CONNACK_RETURNCODE: {code:6, text:"AMQJS0006E Bad Connack return code:{0} {1}."},
174
+ SOCKET_ERROR: {code:7, text:"AMQJS0007E Socket error:{0}."},
175
+ SOCKET_CLOSE: {code:8, text:"AMQJS0008I Socket closed."},
176
+ MALFORMED_UTF: {code:9, text:"AMQJS0009E Malformed UTF data:{0} {1} {2}."},
177
+ UNSUPPORTED: {code:10, text:"AMQJS0010E {0} is not supported by this browser."},
178
+ INVALID_STATE: {code:11, text:"AMQJS0011E Invalid state {0}."},
179
+ INVALID_TYPE: {code:12, text:"AMQJS0012E Invalid type {0} for {1}."},
180
+ INVALID_ARGUMENT: {code:13, text:"AMQJS0013E Invalid argument {0} for {1}."},
181
+ UNSUPPORTED_OPERATION: {code:14, text:"AMQJS0014E Unsupported operation."},
182
+ INVALID_STORED_DATA: {code:15, text:"AMQJS0015E Invalid data in local storage key={0} value={1}."},
183
+ INVALID_MQTT_MESSAGE_TYPE: {code:16, text:"AMQJS0016E Invalid MQTT message type {0}."},
184
+ MALFORMED_UNICODE: {code:17, text:"AMQJS0017E Malformed Unicode string:{0} {1}."},
185
+ };
186
+
187
+ /** CONNACK RC Meaning. */
188
+ var CONNACK_RC = {
189
+ 0:"Connection Accepted",
190
+ 1:"Connection Refused: unacceptable protocol version",
191
+ 2:"Connection Refused: identifier rejected",
192
+ 3:"Connection Refused: server unavailable",
193
+ 4:"Connection Refused: bad user name or password",
194
+ 5:"Connection Refused: not authorized"
195
+ };
196
+
197
+ /**
198
+ * Format an error message text.
199
+ * @private
200
+ * @param {error} ERROR.KEY value above.
201
+ * @param {substitutions} [array] substituted into the text.
202
+ * @return the text with the substitutions made.
203
+ */
204
+ var format = function(error, substitutions) {
205
+ var text = error.text;
206
+ if (substitutions) {
207
+ for (var i=0; i<substitutions.length; i++) {
208
+ field = "{"+i+"}";
209
+ start = text.indexOf(field);
210
+ if(start > 0) {
211
+ var part1 = text.substring(0,start);
212
+ var part2 = text.substring(start+field.length);
213
+ text = part1+substitutions[i]+part2;
214
+ }
215
+ }
216
+ }
217
+ return text;
218
+ };
219
+
220
+ //MQTT protocol and version 6 M Q I s d p 3
221
+ var MqttProtoIdentifierv3 = [0x00,0x06,0x4d,0x51,0x49,0x73,0x64,0x70,0x03];
222
+ //MQTT proto/version for 311 4 M Q T T 4
223
+ var MqttProtoIdentifierv4 = [0x00,0x04,0x4d,0x51,0x54,0x54,0x04];
224
+
225
+ /**
226
+ * Construct an MQTT wire protocol message.
227
+ * @param type MQTT packet type.
228
+ * @param options optional wire message attributes.
229
+ *
230
+ * Optional properties
231
+ *
232
+ * messageIdentifier: message ID in the range [0..65535]
233
+ * payloadMessage: Application Message - PUBLISH only
234
+ * connectStrings: array of 0 or more Strings to be put into the CONNECT payload
235
+ * topics: array of strings (SUBSCRIBE, UNSUBSCRIBE)
236
+ * requestQoS: array of QoS values [0..2]
237
+ *
238
+ * "Flag" properties
239
+ * cleanSession: true if present / false if absent (CONNECT)
240
+ * willMessage: true if present / false if absent (CONNECT)
241
+ * isRetained: true if present / false if absent (CONNECT)
242
+ * userName: true if present / false if absent (CONNECT)
243
+ * password: true if present / false if absent (CONNECT)
244
+ * keepAliveInterval: integer [0..65535] (CONNECT)
245
+ *
246
+ * @private
247
+ * @ignore
248
+ */
249
+ var WireMessage = function (type, options) {
250
+ this.type = type;
251
+ for (var name in options) {
252
+ if (options.hasOwnProperty(name)) {
253
+ this[name] = options[name];
254
+ }
255
+ }
256
+ };
257
+
258
+ WireMessage.prototype.encode = function() {
259
+ // Compute the first byte of the fixed header
260
+ var first = ((this.type & 0x0f) << 4);
261
+
262
+ /*
263
+ * Now calculate the length of the variable header + payload by adding up the lengths
264
+ * of all the component parts
265
+ */
266
+
267
+ remLength = 0;
268
+ topicStrLength = new Array();
269
+
270
+ // if the message contains a messageIdentifier then we need two bytes for that
271
+ if (this.messageIdentifier != undefined)
272
+ remLength += 2;
273
+
274
+ switch(this.type) {
275
+ // If this a Connect then we need to include 12 bytes for its header
276
+ case MESSAGE_TYPE.CONNECT:
277
+ switch(this.mqttVersion) {
278
+ case 3:
279
+ remLength += MqttProtoIdentifierv3.length + 3;
280
+ break;
281
+ case 4:
282
+ remLength += MqttProtoIdentifierv4.length + 3;
283
+ break;
284
+ }
285
+
286
+ remLength += UTF8Length(this.clientId) + 2;
287
+ if (this.willMessage != undefined) {
288
+ remLength += UTF8Length(this.willMessage.destinationName) + 2;
289
+ // Will message is always a string, sent as UTF-8 characters with a preceding length.
290
+ var willMessagePayloadBytes = this.willMessage.payloadBytes;
291
+ if (!(willMessagePayloadBytes instanceof Uint8Array))
292
+ willMessagePayloadBytes = new Uint8Array(payloadBytes);
293
+ remLength += willMessagePayloadBytes.byteLength +2;
294
+ }
295
+ if (this.userName != undefined)
296
+ remLength += UTF8Length(this.userName) + 2;
297
+ if (this.password != undefined)
298
+ remLength += UTF8Length(this.password) + 2;
299
+ break;
300
+
301
+ // Subscribe, Unsubscribe can both contain topic strings
302
+ case MESSAGE_TYPE.SUBSCRIBE:
303
+ first |= 0x02; // Qos = 1;
304
+ for ( var i = 0; i < this.topics.length; i++) {
305
+ topicStrLength[i] = UTF8Length(this.topics[i]);
306
+ remLength += topicStrLength[i] + 2;
307
+ }
308
+ remLength += this.requestedQos.length; // 1 byte for each topic's Qos
309
+ // QoS on Subscribe only
310
+ break;
311
+
312
+ case MESSAGE_TYPE.UNSUBSCRIBE:
313
+ first |= 0x02; // Qos = 1;
314
+ for ( var i = 0; i < this.topics.length; i++) {
315
+ topicStrLength[i] = UTF8Length(this.topics[i]);
316
+ remLength += topicStrLength[i] + 2;
317
+ }
318
+ break;
319
+
320
+ case MESSAGE_TYPE.PUBREL:
321
+ first |= 0x02; // Qos = 1;
322
+ break;
323
+
324
+ case MESSAGE_TYPE.PUBLISH:
325
+ if (this.payloadMessage.duplicate) first |= 0x08;
326
+ first = first |= (this.payloadMessage.qos << 1);
327
+ if (this.payloadMessage.retained) first |= 0x01;
328
+ destinationNameLength = UTF8Length(this.payloadMessage.destinationName);
329
+ remLength += destinationNameLength + 2;
330
+ var payloadBytes = this.payloadMessage.payloadBytes;
331
+ remLength += payloadBytes.byteLength;
332
+ if (payloadBytes instanceof ArrayBuffer)
333
+ payloadBytes = new Uint8Array(payloadBytes);
334
+ else if (!(payloadBytes instanceof Uint8Array))
335
+ payloadBytes = new Uint8Array(payloadBytes.buffer);
336
+ break;
337
+
338
+ case MESSAGE_TYPE.DISCONNECT:
339
+ break;
340
+
341
+ default:
342
+ ;
343
+ }
344
+
345
+ // Now we can allocate a buffer for the message
346
+
347
+ var mbi = encodeMBI(remLength); // Convert the length to MQTT MBI format
348
+ var pos = mbi.length + 1; // Offset of start of variable header
349
+ var buffer = new ArrayBuffer(remLength + pos);
350
+ var byteStream = new Uint8Array(buffer); // view it as a sequence of bytes
351
+
352
+ //Write the fixed header into the buffer
353
+ byteStream[0] = first;
354
+ byteStream.set(mbi,1);
355
+
356
+ // If this is a PUBLISH then the variable header starts with a topic
357
+ if (this.type == MESSAGE_TYPE.PUBLISH)
358
+ pos = writeString(this.payloadMessage.destinationName, destinationNameLength, byteStream, pos);
359
+ // If this is a CONNECT then the variable header contains the protocol name/version, flags and keepalive time
360
+
361
+ else if (this.type == MESSAGE_TYPE.CONNECT) {
362
+ switch (this.mqttVersion) {
363
+ case 3:
364
+ byteStream.set(MqttProtoIdentifierv3, pos);
365
+ pos += MqttProtoIdentifierv3.length;
366
+ break;
367
+ case 4:
368
+ byteStream.set(MqttProtoIdentifierv4, pos);
369
+ pos += MqttProtoIdentifierv4.length;
370
+ break;
371
+ }
372
+ var connectFlags = 0;
373
+ if (this.cleanSession)
374
+ connectFlags = 0x02;
375
+ if (this.willMessage != undefined ) {
376
+ connectFlags |= 0x04;
377
+ connectFlags |= (this.willMessage.qos<<3);
378
+ if (this.willMessage.retained) {
379
+ connectFlags |= 0x20;
380
+ }
381
+ }
382
+ if (this.userName != undefined)
383
+ connectFlags |= 0x80;
384
+ if (this.password != undefined)
385
+ connectFlags |= 0x40;
386
+ byteStream[pos++] = connectFlags;
387
+ pos = writeUint16 (this.keepAliveInterval, byteStream, pos);
388
+ }
389
+
390
+ // Output the messageIdentifier - if there is one
391
+ if (this.messageIdentifier != undefined)
392
+ pos = writeUint16 (this.messageIdentifier, byteStream, pos);
393
+
394
+ switch(this.type) {
395
+ case MESSAGE_TYPE.CONNECT:
396
+ pos = writeString(this.clientId, UTF8Length(this.clientId), byteStream, pos);
397
+ if (this.willMessage != undefined) {
398
+ pos = writeString(this.willMessage.destinationName, UTF8Length(this.willMessage.destinationName), byteStream, pos);
399
+ pos = writeUint16(willMessagePayloadBytes.byteLength, byteStream, pos);
400
+ byteStream.set(willMessagePayloadBytes, pos);
401
+ pos += willMessagePayloadBytes.byteLength;
402
+
403
+ }
404
+ if (this.userName != undefined)
405
+ pos = writeString(this.userName, UTF8Length(this.userName), byteStream, pos);
406
+ if (this.password != undefined)
407
+ pos = writeString(this.password, UTF8Length(this.password), byteStream, pos);
408
+ break;
409
+
410
+ case MESSAGE_TYPE.PUBLISH:
411
+ // PUBLISH has a text or binary payload, if text do not add a 2 byte length field, just the UTF characters.
412
+ byteStream.set(payloadBytes, pos);
413
+
414
+ break;
415
+
416
+ // case MESSAGE_TYPE.PUBREC:
417
+ // case MESSAGE_TYPE.PUBREL:
418
+ // case MESSAGE_TYPE.PUBCOMP:
419
+ // break;
420
+
421
+ case MESSAGE_TYPE.SUBSCRIBE:
422
+ // SUBSCRIBE has a list of topic strings and request QoS
423
+ for (var i=0; i<this.topics.length; i++) {
424
+ pos = writeString(this.topics[i], topicStrLength[i], byteStream, pos);
425
+ byteStream[pos++] = this.requestedQos[i];
426
+ }
427
+ break;
428
+
429
+ case MESSAGE_TYPE.UNSUBSCRIBE:
430
+ // UNSUBSCRIBE has a list of topic strings
431
+ for (var i=0; i<this.topics.length; i++)
432
+ pos = writeString(this.topics[i], topicStrLength[i], byteStream, pos);
433
+ break;
434
+
435
+ default:
436
+ // Do nothing.
437
+ }
438
+
439
+ return buffer;
440
+ }
441
+
442
+ function decodeMessage(input,pos) {
443
+ var startingPos = pos;
444
+ var first = input[pos];
445
+ var type = first >> 4;
446
+ var messageInfo = first &= 0x0f;
447
+ pos += 1;
448
+
449
+
450
+ // Decode the remaining length (MBI format)
451
+
452
+ var digit;
453
+ var remLength = 0;
454
+ var multiplier = 1;
455
+ do {
456
+ if (pos == input.length) {
457
+ return [null,startingPos];
458
+ }
459
+ digit = input[pos++];
460
+ remLength += ((digit & 0x7F) * multiplier);
461
+ multiplier *= 128;
462
+ } while ((digit & 0x80) != 0);
463
+
464
+ var endPos = pos+remLength;
465
+ if (endPos > input.length) {
466
+ return [null,startingPos];
467
+ }
468
+
469
+ var wireMessage = new WireMessage(type);
470
+ switch(type) {
471
+ case MESSAGE_TYPE.CONNACK:
472
+ var connectAcknowledgeFlags = input[pos++];
473
+ if (connectAcknowledgeFlags & 0x01)
474
+ wireMessage.sessionPresent = true;
475
+ wireMessage.returnCode = input[pos++];
476
+ break;
477
+
478
+ case MESSAGE_TYPE.PUBLISH:
479
+ var qos = (messageInfo >> 1) & 0x03;
480
+
481
+ var len = readUint16(input, pos);
482
+ pos += 2;
483
+ var topicName = parseUTF8(input, pos, len);
484
+ pos += len;
485
+ // If QoS 1 or 2 there will be a messageIdentifier
486
+ if (qos > 0) {
487
+ wireMessage.messageIdentifier = readUint16(input, pos);
488
+ pos += 2;
489
+ }
490
+
491
+ var message = new Paho.MQTT.Message(input.subarray(pos, endPos));
492
+ if ((messageInfo & 0x01) == 0x01)
493
+ message.retained = true;
494
+ if ((messageInfo & 0x08) == 0x08)
495
+ message.duplicate = true;
496
+ message.qos = qos;
497
+ message.destinationName = topicName;
498
+ wireMessage.payloadMessage = message;
499
+ break;
500
+
501
+ case MESSAGE_TYPE.PUBACK:
502
+ case MESSAGE_TYPE.PUBREC:
503
+ case MESSAGE_TYPE.PUBREL:
504
+ case MESSAGE_TYPE.PUBCOMP:
505
+ case MESSAGE_TYPE.UNSUBACK:
506
+ wireMessage.messageIdentifier = readUint16(input, pos);
507
+ break;
508
+
509
+ case MESSAGE_TYPE.SUBACK:
510
+ wireMessage.messageIdentifier = readUint16(input, pos);
511
+ pos += 2;
512
+ wireMessage.returnCode = input.subarray(pos, endPos);
513
+ break;
514
+
515
+ default:
516
+ ;
517
+ }
518
+
519
+ return [wireMessage,endPos];
520
+ }
521
+
522
+ function writeUint16(input, buffer, offset) {
523
+ buffer[offset++] = input >> 8; //MSB
524
+ buffer[offset++] = input % 256; //LSB
525
+ return offset;
526
+ }
527
+
528
+ function writeString(input, utf8Length, buffer, offset) {
529
+ offset = writeUint16(utf8Length, buffer, offset);
530
+ stringToUTF8(input, buffer, offset);
531
+ return offset + utf8Length;
532
+ }
533
+
534
+ function readUint16(buffer, offset) {
535
+ return 256*buffer[offset] + buffer[offset+1];
536
+ }
537
+
538
+ /**
539
+ * Encodes an MQTT Multi-Byte Integer
540
+ * @private
541
+ */
542
+ function encodeMBI(number) {
543
+ var output = new Array(1);
544
+ var numBytes = 0;
545
+
546
+ do {
547
+ var digit = number % 128;
548
+ number = number >> 7;
549
+ if (number > 0) {
550
+ digit |= 0x80;
551
+ }
552
+ output[numBytes++] = digit;
553
+ } while ( (number > 0) && (numBytes<4) );
554
+
555
+ return output;
556
+ }
557
+
558
+ /**
559
+ * Takes a String and calculates its length in bytes when encoded in UTF8.
560
+ * @private
561
+ */
562
+ function UTF8Length(input) {
563
+ var output = 0;
564
+ for (var i = 0; i<input.length; i++)
565
+ {
566
+ var charCode = input.charCodeAt(i);
567
+ if (charCode > 0x7FF)
568
+ {
569
+ // Surrogate pair means its a 4 byte character
570
+ if (0xD800 <= charCode && charCode <= 0xDBFF)
571
+ {
572
+ i++;
573
+ output++;
574
+ }
575
+ output +=3;
576
+ }
577
+ else if (charCode > 0x7F)
578
+ output +=2;
579
+ else
580
+ output++;
581
+ }
582
+ return output;
583
+ }
584
+
585
+ /**
586
+ * Takes a String and writes it into an array as UTF8 encoded bytes.
587
+ * @private
588
+ */
589
+ function stringToUTF8(input, output, start) {
590
+ var pos = start;
591
+ for (var i = 0; i<input.length; i++) {
592
+ var charCode = input.charCodeAt(i);
593
+
594
+ // Check for a surrogate pair.
595
+ if (0xD800 <= charCode && charCode <= 0xDBFF) {
596
+ lowCharCode = input.charCodeAt(++i);
597
+ if (isNaN(lowCharCode)) {
598
+ throw new Error(format(ERROR.MALFORMED_UNICODE, [charCode, lowCharCode]));
599
+ }
600
+ charCode = ((charCode - 0xD800)<<10) + (lowCharCode - 0xDC00) + 0x10000;
601
+
602
+ }
603
+
604
+ if (charCode <= 0x7F) {
605
+ output[pos++] = charCode;
606
+ } else if (charCode <= 0x7FF) {
607
+ output[pos++] = charCode>>6 & 0x1F | 0xC0;
608
+ output[pos++] = charCode & 0x3F | 0x80;
609
+ } else if (charCode <= 0xFFFF) {
610
+ output[pos++] = charCode>>12 & 0x0F | 0xE0;
611
+ output[pos++] = charCode>>6 & 0x3F | 0x80;
612
+ output[pos++] = charCode & 0x3F | 0x80;
613
+ } else {
614
+ output[pos++] = charCode>>18 & 0x07 | 0xF0;
615
+ output[pos++] = charCode>>12 & 0x3F | 0x80;
616
+ output[pos++] = charCode>>6 & 0x3F | 0x80;
617
+ output[pos++] = charCode & 0x3F | 0x80;
618
+ };
619
+ }
620
+ return output;
621
+ }
622
+
623
+ function parseUTF8(input, offset, length) {
624
+ var output = "";
625
+ var utf16;
626
+ var pos = offset;
627
+
628
+ while (pos < offset+length)
629
+ {
630
+ var byte1 = input[pos++];
631
+ if (byte1 < 128)
632
+ utf16 = byte1;
633
+ else
634
+ {
635
+ var byte2 = input[pos++]-128;
636
+ if (byte2 < 0)
637
+ throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16),""]));
638
+ if (byte1 < 0xE0) // 2 byte character
639
+ utf16 = 64*(byte1-0xC0) + byte2;
640
+ else
641
+ {
642
+ var byte3 = input[pos++]-128;
643
+ if (byte3 < 0)
644
+ throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16)]));
645
+ if (byte1 < 0xF0) // 3 byte character
646
+ utf16 = 4096*(byte1-0xE0) + 64*byte2 + byte3;
647
+ else
648
+ {
649
+ var byte4 = input[pos++]-128;
650
+ if (byte4 < 0)
651
+ throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)]));
652
+ if (byte1 < 0xF8) // 4 byte character
653
+ utf16 = 262144*(byte1-0xF0) + 4096*byte2 + 64*byte3 + byte4;
654
+ else // longer encodings are not supported
655
+ throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)]));
656
+ }
657
+ }
658
+ }
659
+
660
+ if (utf16 > 0xFFFF) // 4 byte character - express as a surrogate pair
661
+ {
662
+ utf16 -= 0x10000;
663
+ output += String.fromCharCode(0xD800 + (utf16 >> 10)); // lead character
664
+ utf16 = 0xDC00 + (utf16 & 0x3FF); // trail character
665
+ }
666
+ output += String.fromCharCode(utf16);
667
+ }
668
+ return output;
669
+ }
670
+
671
+ /**
672
+ * Repeat keepalive requests, monitor responses.
673
+ * @ignore
674
+ */
675
+ var Pinger = function(client, window, keepAliveInterval) {
676
+ this._client = client;
677
+ this._window = window;
678
+ this._keepAliveInterval = keepAliveInterval*1000;
679
+ this.isReset = false;
680
+
681
+ var pingReq = new WireMessage(MESSAGE_TYPE.PINGREQ).encode();
682
+
683
+ var doTimeout = function (pinger) {
684
+ return function () {
685
+ return doPing.apply(pinger);
686
+ };
687
+ };
688
+
689
+ /** @ignore */
690
+ var doPing = function() {
691
+ if (!this.isReset) {
692
+ this._client._trace("Pinger.doPing", "Timed out");
693
+ this._client._disconnected( ERROR.PING_TIMEOUT.code , format(ERROR.PING_TIMEOUT));
694
+ } else {
695
+ this.isReset = false;
696
+ this._client._trace("Pinger.doPing", "send PINGREQ");
697
+ this._client.socket.send(pingReq);
698
+ this.timeout = this._window.setTimeout(doTimeout(this), this._keepAliveInterval);
699
+ }
700
+ }
701
+
702
+ this.reset = function() {
703
+ this.isReset = true;
704
+ this._window.clearTimeout(this.timeout);
705
+ if (this._keepAliveInterval > 0)
706
+ this.timeout = setTimeout(doTimeout(this), this._keepAliveInterval);
707
+ }
708
+
709
+ this.cancel = function() {
710
+ this._window.clearTimeout(this.timeout);
711
+ }
712
+ };
713
+
714
+ /**
715
+ * Monitor request completion.
716
+ * @ignore
717
+ */
718
+ var Timeout = function(client, window, timeoutSeconds, action, args) {
719
+ this._window = window;
720
+ if (!timeoutSeconds)
721
+ timeoutSeconds = 30;
722
+
723
+ var doTimeout = function (action, client, args) {
724
+ return function () {
725
+ return action.apply(client, args);
726
+ };
727
+ };
728
+ this.timeout = setTimeout(doTimeout(action, client, args), timeoutSeconds * 1000);
729
+
730
+ this.cancel = function() {
731
+ this._window.clearTimeout(this.timeout);
732
+ }
733
+ };
734
+
735
+ /*
736
+ * Internal implementation of the Websockets MQTT V3.1 client.
737
+ *
738
+ * @name Paho.MQTT.ClientImpl @constructor
739
+ * @param {String} host the DNS nameof the webSocket host.
740
+ * @param {Number} port the port number for that host.
741
+ * @param {String} clientId the MQ client identifier.
742
+ */
743
+ var ClientImpl = function (uri, host, port, path, clientId) {
744
+ // Check dependencies are satisfied in this browser.
745
+ if (!("WebSocket" in global && global["WebSocket"] !== null)) {
746
+ throw new Error(format(ERROR.UNSUPPORTED, ["WebSocket"]));
747
+ }
748
+ if (!("localStorage" in global && global["localStorage"] !== null)) {
749
+ throw new Error(format(ERROR.UNSUPPORTED, ["localStorage"]));
750
+ }
751
+ if (!("ArrayBuffer" in global && global["ArrayBuffer"] !== null)) {
752
+ throw new Error(format(ERROR.UNSUPPORTED, ["ArrayBuffer"]));
753
+ }
754
+ this._trace("Paho.MQTT.Client", uri, host, port, path, clientId);
755
+
756
+ this.host = host;
757
+ this.port = port;
758
+ this.path = path;
759
+ this.uri = uri;
760
+ this.clientId = clientId;
761
+
762
+ // Local storagekeys are qualified with the following string.
763
+ // The conditional inclusion of path in the key is for backward
764
+ // compatibility to when the path was not configurable and assumed to
765
+ // be /mqtt
766
+ this._localKey=host+":"+port+(path!="/mqtt"?":"+path:"")+":"+clientId+":";
767
+
768
+ // Create private instance-only message queue
769
+ // Internal queue of messages to be sent, in sending order.
770
+ this._msg_queue = [];
771
+
772
+ // Messages we have sent and are expecting a response for, indexed by their respective message ids.
773
+ this._sentMessages = {};
774
+
775
+ // Messages we have received and acknowleged and are expecting a confirm message for
776
+ // indexed by their respective message ids.
777
+ this._receivedMessages = {};
778
+
779
+ // Internal list of callbacks to be executed when messages
780
+ // have been successfully sent over web socket, e.g. disconnect
781
+ // when it doesn't have to wait for ACK, just message is dispatched.
782
+ this._notify_msg_sent = {};
783
+
784
+ // Unique identifier for SEND messages, incrementing
785
+ // counter as messages are sent.
786
+ this._message_identifier = 1;
787
+
788
+ // Used to determine the transmission sequence of stored sent messages.
789
+ this._sequence = 0;
790
+
791
+
792
+ // Load the local state, if any, from the saved version, only restore state relevant to this client.
793
+ for (var key in localStorage)
794
+ if ( key.indexOf("Sent:"+this._localKey) == 0
795
+ || key.indexOf("Received:"+this._localKey) == 0)
796
+ this.restore(key);
797
+ };
798
+
799
+ // Messaging Client public instance members.
800
+ ClientImpl.prototype.host;
801
+ ClientImpl.prototype.port;
802
+ ClientImpl.prototype.path;
803
+ ClientImpl.prototype.uri;
804
+ ClientImpl.prototype.clientId;
805
+
806
+ // Messaging Client private instance members.
807
+ ClientImpl.prototype.socket;
808
+ /* true once we have received an acknowledgement to a CONNECT packet. */
809
+ ClientImpl.prototype.connected = false;
810
+ /* The largest message identifier allowed, may not be larger than 2**16 but
811
+ * if set smaller reduces the maximum number of outbound messages allowed.
812
+ */
813
+ ClientImpl.prototype.maxMessageIdentifier = 65536;
814
+ ClientImpl.prototype.connectOptions;
815
+ ClientImpl.prototype.hostIndex;
816
+ ClientImpl.prototype.onConnectionLost;
817
+ ClientImpl.prototype.onMessageDelivered;
818
+ ClientImpl.prototype.onMessageArrived;
819
+ ClientImpl.prototype._msg_queue = null;
820
+ ClientImpl.prototype._connectTimeout;
821
+ /* The sendPinger monitors how long we allow before we send data to prove to the server that we are alive. */
822
+ ClientImpl.prototype.sendPinger = null;
823
+ /* The receivePinger monitors how long we allow before we require evidence that the server is alive. */
824
+ ClientImpl.prototype.receivePinger = null;
825
+
826
+ ClientImpl.prototype.receiveBuffer = null;
827
+
828
+ ClientImpl.prototype._traceBuffer = null;
829
+ ClientImpl.prototype._MAX_TRACE_ENTRIES = 100;
830
+
831
+ ClientImpl.prototype.connect = function (connectOptions) {
832
+ var connectOptionsMasked = this._traceMask(connectOptions, "password");
833
+ this._trace("Client.connect", connectOptionsMasked, this.socket, this.connected);
834
+
835
+ if (this.connected)
836
+ throw new Error(format(ERROR.INVALID_STATE, ["already connected"]));
837
+ if (this.socket)
838
+ throw new Error(format(ERROR.INVALID_STATE, ["already connected"]));
839
+
840
+ this.connectOptions = connectOptions;
841
+
842
+ if (connectOptions.uris) {
843
+ this.hostIndex = 0;
844
+ this._doConnect(connectOptions.uris[0]);
845
+ } else {
846
+ this._doConnect(this.uri);
847
+ }
848
+
849
+ };
850
+
851
+ ClientImpl.prototype.subscribe = function (filter, subscribeOptions) {
852
+ this._trace("Client.subscribe", filter, subscribeOptions);
853
+
854
+ if (!this.connected)
855
+ throw new Error(format(ERROR.INVALID_STATE, ["not connected"]));
856
+
857
+ var wireMessage = new WireMessage(MESSAGE_TYPE.SUBSCRIBE);
858
+ wireMessage.topics=[filter];
859
+ if (subscribeOptions.qos != undefined)
860
+ wireMessage.requestedQos = [subscribeOptions.qos];
861
+ else
862
+ wireMessage.requestedQos = [0];
863
+
864
+ if (subscribeOptions.onSuccess) {
865
+ wireMessage.onSuccess = function(grantedQos) {subscribeOptions.onSuccess({invocationContext:subscribeOptions.invocationContext,grantedQos:grantedQos});};
866
+ }
867
+
868
+ if (subscribeOptions.onFailure) {
869
+ wireMessage.onFailure = function(errorCode) {subscribeOptions.onFailure({invocationContext:subscribeOptions.invocationContext,errorCode:errorCode});};
870
+ }
871
+
872
+ if (subscribeOptions.timeout) {
873
+ wireMessage.timeOut = new Timeout(this, window, subscribeOptions.timeout, subscribeOptions.onFailure
874
+ , [{invocationContext:subscribeOptions.invocationContext,
875
+ errorCode:ERROR.SUBSCRIBE_TIMEOUT.code,
876
+ errorMessage:format(ERROR.SUBSCRIBE_TIMEOUT)}]);
877
+ }
878
+
879
+ // All subscriptions return a SUBACK.
880
+ this._requires_ack(wireMessage);
881
+ this._schedule_message(wireMessage);
882
+ };
883
+
884
+ /** @ignore */
885
+ ClientImpl.prototype.unsubscribe = function(filter, unsubscribeOptions) {
886
+ this._trace("Client.unsubscribe", filter, unsubscribeOptions);
887
+
888
+ if (!this.connected)
889
+ throw new Error(format(ERROR.INVALID_STATE, ["not connected"]));
890
+
891
+ var wireMessage = new WireMessage(MESSAGE_TYPE.UNSUBSCRIBE);
892
+ wireMessage.topics = [filter];
893
+
894
+ if (unsubscribeOptions.onSuccess) {
895
+ wireMessage.callback = function() {unsubscribeOptions.onSuccess({invocationContext:unsubscribeOptions.invocationContext});};
896
+ }
897
+ if (unsubscribeOptions.timeout) {
898
+ wireMessage.timeOut = new Timeout(this, window, unsubscribeOptions.timeout, unsubscribeOptions.onFailure
899
+ , [{invocationContext:unsubscribeOptions.invocationContext,
900
+ errorCode:ERROR.UNSUBSCRIBE_TIMEOUT.code,
901
+ errorMessage:format(ERROR.UNSUBSCRIBE_TIMEOUT)}]);
902
+ }
903
+
904
+ // All unsubscribes return a SUBACK.
905
+ this._requires_ack(wireMessage);
906
+ this._schedule_message(wireMessage);
907
+ };
908
+
909
+ ClientImpl.prototype.send = function (message) {
910
+ this._trace("Client.send", message);
911
+
912
+ if (!this.connected)
913
+ throw new Error(format(ERROR.INVALID_STATE, ["not connected"]));
914
+
915
+ wireMessage = new WireMessage(MESSAGE_TYPE.PUBLISH);
916
+ wireMessage.payloadMessage = message;
917
+
918
+ if (message.qos > 0)
919
+ this._requires_ack(wireMessage);
920
+ else if (this.onMessageDelivered)
921
+ this._notify_msg_sent[wireMessage] = this.onMessageDelivered(wireMessage.payloadMessage);
922
+ this._schedule_message(wireMessage);
923
+ };
924
+
925
+ ClientImpl.prototype.disconnect = function () {
926
+ this._trace("Client.disconnect");
927
+
928
+ if (!this.socket)
929
+ throw new Error(format(ERROR.INVALID_STATE, ["not connecting or connected"]));
930
+
931
+ wireMessage = new WireMessage(MESSAGE_TYPE.DISCONNECT);
932
+
933
+ // Run the disconnected call back as soon as the message has been sent,
934
+ // in case of a failure later on in the disconnect processing.
935
+ // as a consequence, the _disconected call back may be run several times.
936
+ this._notify_msg_sent[wireMessage] = scope(this._disconnected, this);
937
+
938
+ this._schedule_message(wireMessage);
939
+ };
940
+
941
+ ClientImpl.prototype.getTraceLog = function () {
942
+ if ( this._traceBuffer !== null ) {
943
+ this._trace("Client.getTraceLog", new Date());
944
+ this._trace("Client.getTraceLog in flight messages", this._sentMessages.length);
945
+ for (var key in this._sentMessages)
946
+ this._trace("_sentMessages ",key, this._sentMessages[key]);
947
+ for (var key in this._receivedMessages)
948
+ this._trace("_receivedMessages ",key, this._receivedMessages[key]);
949
+
950
+ return this._traceBuffer;
951
+ }
952
+ };
953
+
954
+ ClientImpl.prototype.startTrace = function () {
955
+ if ( this._traceBuffer === null ) {
956
+ this._traceBuffer = [];
957
+ }
958
+ this._trace("Client.startTrace", new Date(), version);
959
+ };
960
+
961
+ ClientImpl.prototype.stopTrace = function () {
962
+ delete this._traceBuffer;
963
+ };
964
+
965
+ ClientImpl.prototype._doConnect = function (wsurl) {
966
+ // When the socket is open, this client will send the CONNECT WireMessage using the saved parameters.
967
+ if (this.connectOptions.useSSL) {
968
+ var uriParts = wsurl.split(":");
969
+ uriParts[0] = "wss";
970
+ wsurl = uriParts.join(":");
971
+ }
972
+ this.connected = false;
973
+ this.socket = new WebSocket(wsurl, ["mqtt","mqttv3.1"]);
974
+ this.socket.binaryType = 'arraybuffer';
975
+
976
+ this.socket.onopen = scope(this._on_socket_open, this);
977
+ this.socket.onmessage = scope(this._on_socket_message, this);
978
+ this.socket.onerror = scope(this._on_socket_error, this);
979
+ this.socket.onclose = scope(this._on_socket_close, this);
980
+
981
+ this.sendPinger = new Pinger(this, window, this.connectOptions.keepAliveInterval);
982
+ this.receivePinger = new Pinger(this, window, this.connectOptions.keepAliveInterval);
983
+
984
+ this._connectTimeout = new Timeout(this, window, this.connectOptions.timeout, this._disconnected, [ERROR.CONNECT_TIMEOUT.code, format(ERROR.CONNECT_TIMEOUT)]);
985
+ };
986
+
987
+
988
+ // Schedule a new message to be sent over the WebSockets
989
+ // connection. CONNECT messages cause WebSocket connection
990
+ // to be started. All other messages are queued internally
991
+ // until this has happened. When WS connection starts, process
992
+ // all outstanding messages.
993
+ ClientImpl.prototype._schedule_message = function (message) {
994
+ this._msg_queue.push(message);
995
+ // Process outstanding messages in the queue if we have an open socket, and have received CONNACK.
996
+ if (this.connected) {
997
+ this._process_queue();
998
+ }
999
+ };
1000
+
1001
+ ClientImpl.prototype.store = function(prefix, wireMessage) {
1002
+ storedMessage = {type:wireMessage.type, messageIdentifier:wireMessage.messageIdentifier, version:1};
1003
+
1004
+ switch(wireMessage.type) {
1005
+ case MESSAGE_TYPE.PUBLISH:
1006
+ if(wireMessage.pubRecReceived)
1007
+ storedMessage.pubRecReceived = true;
1008
+
1009
+ // Convert the payload to a hex string.
1010
+ storedMessage.payloadMessage = {};
1011
+ var hex = "";
1012
+ var messageBytes = wireMessage.payloadMessage.payloadBytes;
1013
+ for (var i=0; i<messageBytes.length; i++) {
1014
+ if (messageBytes[i] <= 0xF)
1015
+ hex = hex+"0"+messageBytes[i].toString(16);
1016
+ else
1017
+ hex = hex+messageBytes[i].toString(16);
1018
+ }
1019
+ storedMessage.payloadMessage.payloadHex = hex;
1020
+
1021
+ storedMessage.payloadMessage.qos = wireMessage.payloadMessage.qos;
1022
+ storedMessage.payloadMessage.destinationName = wireMessage.payloadMessage.destinationName;
1023
+ if (wireMessage.payloadMessage.duplicate)
1024
+ storedMessage.payloadMessage.duplicate = true;
1025
+ if (wireMessage.payloadMessage.retained)
1026
+ storedMessage.payloadMessage.retained = true;
1027
+
1028
+ // Add a sequence number to sent messages.
1029
+ if ( prefix.indexOf("Sent:") == 0 ) {
1030
+ if ( wireMessage.sequence === undefined )
1031
+ wireMessage.sequence = ++this._sequence;
1032
+ storedMessage.sequence = wireMessage.sequence;
1033
+ }
1034
+ break;
1035
+
1036
+ default:
1037
+ throw Error(format(ERROR.INVALID_STORED_DATA, [key, storedMessage]));
1038
+ }
1039
+ localStorage.setItem(prefix+this._localKey+wireMessage.messageIdentifier, JSON.stringify(storedMessage));
1040
+ };
1041
+
1042
+ ClientImpl.prototype.restore = function(key) {
1043
+ var value = localStorage.getItem(key);
1044
+ var storedMessage = JSON.parse(value);
1045
+
1046
+ var wireMessage = new WireMessage(storedMessage.type, storedMessage);
1047
+
1048
+ switch(storedMessage.type) {
1049
+ case MESSAGE_TYPE.PUBLISH:
1050
+ // Replace the payload message with a Message object.
1051
+ var hex = storedMessage.payloadMessage.payloadHex;
1052
+ var buffer = new ArrayBuffer((hex.length)/2);
1053
+ var byteStream = new Uint8Array(buffer);
1054
+ var i = 0;
1055
+ while (hex.length >= 2) {
1056
+ var x = parseInt(hex.substring(0, 2), 16);
1057
+ hex = hex.substring(2, hex.length);
1058
+ byteStream[i++] = x;
1059
+ }
1060
+ var payloadMessage = new Paho.MQTT.Message(byteStream);
1061
+
1062
+ payloadMessage.qos = storedMessage.payloadMessage.qos;
1063
+ payloadMessage.destinationName = storedMessage.payloadMessage.destinationName;
1064
+ if (storedMessage.payloadMessage.duplicate)
1065
+ payloadMessage.duplicate = true;
1066
+ if (storedMessage.payloadMessage.retained)
1067
+ payloadMessage.retained = true;
1068
+ wireMessage.payloadMessage = payloadMessage;
1069
+
1070
+ break;
1071
+
1072
+ default:
1073
+ throw Error(format(ERROR.INVALID_STORED_DATA, [key, value]));
1074
+ }
1075
+
1076
+ if (key.indexOf("Sent:"+this._localKey) == 0) {
1077
+ wireMessage.payloadMessage.duplicate = true;
1078
+ this._sentMessages[wireMessage.messageIdentifier] = wireMessage;
1079
+ } else if (key.indexOf("Received:"+this._localKey) == 0) {
1080
+ this._receivedMessages[wireMessage.messageIdentifier] = wireMessage;
1081
+ }
1082
+ };
1083
+
1084
+ ClientImpl.prototype._process_queue = function () {
1085
+ var message = null;
1086
+ // Process messages in order they were added
1087
+ var fifo = this._msg_queue.reverse();
1088
+
1089
+ // Send all queued messages down socket connection
1090
+ while ((message = fifo.pop())) {
1091
+ this._socket_send(message);
1092
+ // Notify listeners that message was successfully sent
1093
+ if (this._notify_msg_sent[message]) {
1094
+ this._notify_msg_sent[message]();
1095
+ delete this._notify_msg_sent[message];
1096
+ }
1097
+ }
1098
+ };
1099
+
1100
+ /**
1101
+ * Expect an ACK response for this message. Add message to the set of in progress
1102
+ * messages and set an unused identifier in this message.
1103
+ * @ignore
1104
+ */
1105
+ ClientImpl.prototype._requires_ack = function (wireMessage) {
1106
+ var messageCount = Object.keys(this._sentMessages).length;
1107
+ if (messageCount > this.maxMessageIdentifier)
1108
+ throw Error ("Too many messages:"+messageCount);
1109
+
1110
+ while(this._sentMessages[this._message_identifier] !== undefined) {
1111
+ this._message_identifier++;
1112
+ }
1113
+ wireMessage.messageIdentifier = this._message_identifier;
1114
+ this._sentMessages[wireMessage.messageIdentifier] = wireMessage;
1115
+ if (wireMessage.type === MESSAGE_TYPE.PUBLISH) {
1116
+ this.store("Sent:", wireMessage);
1117
+ }
1118
+ if (this._message_identifier === this.maxMessageIdentifier) {
1119
+ this._message_identifier = 1;
1120
+ }
1121
+ };
1122
+
1123
+ /**
1124
+ * Called when the underlying websocket has been opened.
1125
+ * @ignore
1126
+ */
1127
+ ClientImpl.prototype._on_socket_open = function () {
1128
+ // Create the CONNECT message object.
1129
+ var wireMessage = new WireMessage(MESSAGE_TYPE.CONNECT, this.connectOptions);
1130
+ wireMessage.clientId = this.clientId;
1131
+ this._socket_send(wireMessage);
1132
+ };
1133
+
1134
+ /**
1135
+ * Called when the underlying websocket has received a complete packet.
1136
+ * @ignore
1137
+ */
1138
+ ClientImpl.prototype._on_socket_message = function (event) {
1139
+ this._trace("Client._on_socket_message", event.data);
1140
+ // Reset the receive ping timer, we now have evidence the server is alive.
1141
+ this.receivePinger.reset();
1142
+ var messages = this._deframeMessages(event.data);
1143
+ for (var i = 0; i < messages.length; i+=1) {
1144
+ this._handleMessage(messages[i]);
1145
+ }
1146
+ }
1147
+
1148
+ ClientImpl.prototype._deframeMessages = function(data) {
1149
+ var byteArray = new Uint8Array(data);
1150
+ if (this.receiveBuffer) {
1151
+ var newData = new Uint8Array(this.receiveBuffer.length+byteArray.length);
1152
+ newData.set(this.receiveBuffer);
1153
+ newData.set(byteArray,this.receiveBuffer.length);
1154
+ byteArray = newData;
1155
+ delete this.receiveBuffer;
1156
+ }
1157
+ try {
1158
+ var offset = 0;
1159
+ var messages = [];
1160
+ while(offset < byteArray.length) {
1161
+ var result = decodeMessage(byteArray,offset);
1162
+ var wireMessage = result[0];
1163
+ offset = result[1];
1164
+ if (wireMessage !== null) {
1165
+ messages.push(wireMessage);
1166
+ } else {
1167
+ break;
1168
+ }
1169
+ }
1170
+ if (offset < byteArray.length) {
1171
+ this.receiveBuffer = byteArray.subarray(offset);
1172
+ }
1173
+ } catch (error) {
1174
+ this._disconnected(ERROR.INTERNAL_ERROR.code , format(ERROR.INTERNAL_ERROR, [error.message]));
1175
+ return;
1176
+ }
1177
+ return messages;
1178
+ }
1179
+
1180
+ ClientImpl.prototype._handleMessage = function(wireMessage) {
1181
+
1182
+ this._trace("Client._handleMessage", wireMessage);
1183
+
1184
+ try {
1185
+ switch(wireMessage.type) {
1186
+ case MESSAGE_TYPE.CONNACK:
1187
+ this._connectTimeout.cancel();
1188
+
1189
+ // If we have started using clean session then clear up the local state.
1190
+ if (this.connectOptions.cleanSession) {
1191
+ for (var key in this._sentMessages) {
1192
+ var sentMessage = this._sentMessages[key];
1193
+ localStorage.removeItem("Sent:"+this._localKey+sentMessage.messageIdentifier);
1194
+ }
1195
+ this._sentMessages = {};
1196
+
1197
+ for (var key in this._receivedMessages) {
1198
+ var receivedMessage = this._receivedMessages[key];
1199
+ localStorage.removeItem("Received:"+this._localKey+receivedMessage.messageIdentifier);
1200
+ }
1201
+ this._receivedMessages = {};
1202
+ }
1203
+ // Client connected and ready for business.
1204
+ if (wireMessage.returnCode === 0) {
1205
+ this.connected = true;
1206
+ // Jump to the end of the list of uris and stop looking for a good host.
1207
+ if (this.connectOptions.uris)
1208
+ this.hostIndex = this.connectOptions.uris.length;
1209
+ } else {
1210
+ this._disconnected(ERROR.CONNACK_RETURNCODE.code , format(ERROR.CONNACK_RETURNCODE, [wireMessage.returnCode, CONNACK_RC[wireMessage.returnCode]]));
1211
+ break;
1212
+ }
1213
+
1214
+ // Resend messages.
1215
+ var sequencedMessages = new Array();
1216
+ for (var msgId in this._sentMessages) {
1217
+ if (this._sentMessages.hasOwnProperty(msgId))
1218
+ sequencedMessages.push(this._sentMessages[msgId]);
1219
+ }
1220
+
1221
+ // Sort sentMessages into the original sent order.
1222
+ var sequencedMessages = sequencedMessages.sort(function(a,b) {return a.sequence - b.sequence;} );
1223
+ for (var i=0, len=sequencedMessages.length; i<len; i++) {
1224
+ var sentMessage = sequencedMessages[i];
1225
+ if (sentMessage.type == MESSAGE_TYPE.PUBLISH && sentMessage.pubRecReceived) {
1226
+ var pubRelMessage = new WireMessage(MESSAGE_TYPE.PUBREL, {messageIdentifier:sentMessage.messageIdentifier});
1227
+ this._schedule_message(pubRelMessage);
1228
+ } else {
1229
+ this._schedule_message(sentMessage);
1230
+ };
1231
+ }
1232
+
1233
+ // Execute the connectOptions.onSuccess callback if there is one.
1234
+ if (this.connectOptions.onSuccess) {
1235
+ this.connectOptions.onSuccess({invocationContext:this.connectOptions.invocationContext});
1236
+ }
1237
+
1238
+ // Process all queued messages now that the connection is established.
1239
+ this._process_queue();
1240
+ break;
1241
+
1242
+ case MESSAGE_TYPE.PUBLISH:
1243
+ this._receivePublish(wireMessage);
1244
+ break;
1245
+
1246
+ case MESSAGE_TYPE.PUBACK:
1247
+ var sentMessage = this._sentMessages[wireMessage.messageIdentifier];
1248
+ // If this is a re flow of a PUBACK after we have restarted receivedMessage will not exist.
1249
+ if (sentMessage) {
1250
+ delete this._sentMessages[wireMessage.messageIdentifier];
1251
+ localStorage.removeItem("Sent:"+this._localKey+wireMessage.messageIdentifier);
1252
+ if (this.onMessageDelivered)
1253
+ this.onMessageDelivered(sentMessage.payloadMessage);
1254
+ }
1255
+ break;
1256
+
1257
+ case MESSAGE_TYPE.PUBREC:
1258
+ var sentMessage = this._sentMessages[wireMessage.messageIdentifier];
1259
+ // If this is a re flow of a PUBREC after we have restarted receivedMessage will not exist.
1260
+ if (sentMessage) {
1261
+ sentMessage.pubRecReceived = true;
1262
+ var pubRelMessage = new WireMessage(MESSAGE_TYPE.PUBREL, {messageIdentifier:wireMessage.messageIdentifier});
1263
+ this.store("Sent:", sentMessage);
1264
+ this._schedule_message(pubRelMessage);
1265
+ }
1266
+ break;
1267
+
1268
+ case MESSAGE_TYPE.PUBREL:
1269
+ var receivedMessage = this._receivedMessages[wireMessage.messageIdentifier];
1270
+ localStorage.removeItem("Received:"+this._localKey+wireMessage.messageIdentifier);
1271
+ // If this is a re flow of a PUBREL after we have restarted receivedMessage will not exist.
1272
+ if (receivedMessage) {
1273
+ this._receiveMessage(receivedMessage);
1274
+ delete this._receivedMessages[wireMessage.messageIdentifier];
1275
+ }
1276
+ // Always flow PubComp, we may have previously flowed PubComp but the server lost it and restarted.
1277
+ pubCompMessage = new WireMessage(MESSAGE_TYPE.PUBCOMP, {messageIdentifier:wireMessage.messageIdentifier});
1278
+ this._schedule_message(pubCompMessage);
1279
+
1280
+
1281
+ break;
1282
+
1283
+ case MESSAGE_TYPE.PUBCOMP:
1284
+ var sentMessage = this._sentMessages[wireMessage.messageIdentifier];
1285
+ delete this._sentMessages[wireMessage.messageIdentifier];
1286
+ localStorage.removeItem("Sent:"+this._localKey+wireMessage.messageIdentifier);
1287
+ if (this.onMessageDelivered)
1288
+ this.onMessageDelivered(sentMessage.payloadMessage);
1289
+ break;
1290
+
1291
+ case MESSAGE_TYPE.SUBACK:
1292
+ var sentMessage = this._sentMessages[wireMessage.messageIdentifier];
1293
+ if (sentMessage) {
1294
+ if(sentMessage.timeOut)
1295
+ sentMessage.timeOut.cancel();
1296
+ wireMessage.returnCode.indexOf = Array.prototype.indexOf;
1297
+ if (wireMessage.returnCode.indexOf(0x80) !== -1) {
1298
+ if (sentMessage.onFailure) {
1299
+ sentMessage.onFailure(wireMessage.returnCode);
1300
+ }
1301
+ } else if (sentMessage.onSuccess) {
1302
+ sentMessage.onSuccess(wireMessage.returnCode);
1303
+ }
1304
+ delete this._sentMessages[wireMessage.messageIdentifier];
1305
+ }
1306
+ break;
1307
+
1308
+ case MESSAGE_TYPE.UNSUBACK:
1309
+ var sentMessage = this._sentMessages[wireMessage.messageIdentifier];
1310
+ if (sentMessage) {
1311
+ if (sentMessage.timeOut)
1312
+ sentMessage.timeOut.cancel();
1313
+ if (sentMessage.callback) {
1314
+ sentMessage.callback();
1315
+ }
1316
+ delete this._sentMessages[wireMessage.messageIdentifier];
1317
+ }
1318
+
1319
+ break;
1320
+
1321
+ case MESSAGE_TYPE.PINGRESP:
1322
+ /* The sendPinger or receivePinger may have sent a ping, the receivePinger has already been reset. */
1323
+ this.sendPinger.reset();
1324
+ break;
1325
+
1326
+ case MESSAGE_TYPE.DISCONNECT:
1327
+ // Clients do not expect to receive disconnect packets.
1328
+ this._disconnected(ERROR.INVALID_MQTT_MESSAGE_TYPE.code , format(ERROR.INVALID_MQTT_MESSAGE_TYPE, [wireMessage.type]));
1329
+ break;
1330
+
1331
+ default:
1332
+ this._disconnected(ERROR.INVALID_MQTT_MESSAGE_TYPE.code , format(ERROR.INVALID_MQTT_MESSAGE_TYPE, [wireMessage.type]));
1333
+ };
1334
+ } catch (error) {
1335
+ this._disconnected(ERROR.INTERNAL_ERROR.code , format(ERROR.INTERNAL_ERROR, [error.message]));
1336
+ return;
1337
+ }
1338
+ };
1339
+
1340
+ /** @ignore */
1341
+ ClientImpl.prototype._on_socket_error = function (error) {
1342
+ this._disconnected(ERROR.SOCKET_ERROR.code , format(ERROR.SOCKET_ERROR, [error.data]));
1343
+ };
1344
+
1345
+ /** @ignore */
1346
+ ClientImpl.prototype._on_socket_close = function () {
1347
+ this._disconnected(ERROR.SOCKET_CLOSE.code , format(ERROR.SOCKET_CLOSE));
1348
+ };
1349
+
1350
+ /** @ignore */
1351
+ ClientImpl.prototype._socket_send = function (wireMessage) {
1352
+
1353
+ if (wireMessage.type == 1) {
1354
+ var wireMessageMasked = this._traceMask(wireMessage, "password");
1355
+ this._trace("Client._socket_send", wireMessageMasked);
1356
+ }
1357
+ else this._trace("Client._socket_send", wireMessage);
1358
+
1359
+ this.socket.send(wireMessage.encode());
1360
+ /* We have proved to the server we are alive. */
1361
+ this.sendPinger.reset();
1362
+ };
1363
+
1364
+ /** @ignore */
1365
+ ClientImpl.prototype._receivePublish = function (wireMessage) {
1366
+ switch(wireMessage.payloadMessage.qos) {
1367
+ case "undefined":
1368
+ case 0:
1369
+ this._receiveMessage(wireMessage);
1370
+ break;
1371
+
1372
+ case 1:
1373
+ var pubAckMessage = new WireMessage(MESSAGE_TYPE.PUBACK, {messageIdentifier:wireMessage.messageIdentifier});
1374
+ this._schedule_message(pubAckMessage);
1375
+ this._receiveMessage(wireMessage);
1376
+ break;
1377
+
1378
+ case 2:
1379
+ this._receivedMessages[wireMessage.messageIdentifier] = wireMessage;
1380
+ this.store("Received:", wireMessage);
1381
+ var pubRecMessage = new WireMessage(MESSAGE_TYPE.PUBREC, {messageIdentifier:wireMessage.messageIdentifier});
1382
+ this._schedule_message(pubRecMessage);
1383
+
1384
+ break;
1385
+
1386
+ default:
1387
+ throw Error("Invaild qos="+wireMmessage.payloadMessage.qos);
1388
+ };
1389
+ };
1390
+
1391
+ /** @ignore */
1392
+ ClientImpl.prototype._receiveMessage = function (wireMessage) {
1393
+ if (this.onMessageArrived) {
1394
+ this.onMessageArrived(wireMessage.payloadMessage);
1395
+ }
1396
+ };
1397
+
1398
+ /**
1399
+ * Client has disconnected either at its own request or because the server
1400
+ * or network disconnected it. Remove all non-durable state.
1401
+ * @param {errorCode} [number] the error number.
1402
+ * @param {errorText} [string] the error text.
1403
+ * @ignore
1404
+ */
1405
+ ClientImpl.prototype._disconnected = function (errorCode, errorText) {
1406
+ this._trace("Client._disconnected", errorCode, errorText);
1407
+
1408
+ this.sendPinger.cancel();
1409
+ this.receivePinger.cancel();
1410
+ if (this._connectTimeout)
1411
+ this._connectTimeout.cancel();
1412
+ // Clear message buffers.
1413
+ this._msg_queue = [];
1414
+ this._notify_msg_sent = {};
1415
+
1416
+ if (this.socket) {
1417
+ // Cancel all socket callbacks so that they cannot be driven again by this socket.
1418
+ this.socket.onopen = null;
1419
+ this.socket.onmessage = null;
1420
+ this.socket.onerror = null;
1421
+ this.socket.onclose = null;
1422
+ if (this.socket.readyState === 1)
1423
+ this.socket.close();
1424
+ delete this.socket;
1425
+ }
1426
+
1427
+ if (this.connectOptions.uris && this.hostIndex < this.connectOptions.uris.length-1) {
1428
+ // Try the next host.
1429
+ this.hostIndex++;
1430
+ this._doConnect(this.connectOptions.uris[this.hostIndex]);
1431
+
1432
+ } else {
1433
+
1434
+ if (errorCode === undefined) {
1435
+ errorCode = ERROR.OK.code;
1436
+ errorText = format(ERROR.OK);
1437
+ }
1438
+
1439
+ // Run any application callbacks last as they may attempt to reconnect and hence create a new socket.
1440
+ if (this.connected) {
1441
+ this.connected = false;
1442
+ // Execute the connectionLostCallback if there is one, and we were connected.
1443
+ if (this.onConnectionLost)
1444
+ this.onConnectionLost({errorCode:errorCode, errorMessage:errorText});
1445
+ } else {
1446
+ // Otherwise we never had a connection, so indicate that the connect has failed.
1447
+ if (this.connectOptions.mqttVersion === 4 && this.connectOptions.mqttVersionExplicit === false) {
1448
+ this._trace("Failed to connect V4, dropping back to V3")
1449
+ this.connectOptions.mqttVersion = 3;
1450
+ if (this.connectOptions.uris) {
1451
+ this.hostIndex = 0;
1452
+ this._doConnect(this.connectOptions.uris[0]);
1453
+ } else {
1454
+ this._doConnect(this.uri);
1455
+ }
1456
+ } else if(this.connectOptions.onFailure) {
1457
+ this.connectOptions.onFailure({invocationContext:this.connectOptions.invocationContext, errorCode:errorCode, errorMessage:errorText});
1458
+ }
1459
+ }
1460
+ }
1461
+ };
1462
+
1463
+ /** @ignore */
1464
+ ClientImpl.prototype._trace = function () {
1465
+ if ( this._traceBuffer !== null ) {
1466
+ for (var i = 0, max = arguments.length; i < max; i++) {
1467
+ if ( this._traceBuffer.length == this._MAX_TRACE_ENTRIES ) {
1468
+ this._traceBuffer.shift();
1469
+ }
1470
+ if (i === 0) this._traceBuffer.push(arguments[i]);
1471
+ else if (typeof arguments[i] === "undefined" ) this._traceBuffer.push(arguments[i]);
1472
+ else this._traceBuffer.push(" "+JSON.stringify(arguments[i]));
1473
+ };
1474
+ };
1475
+ };
1476
+
1477
+ /** @ignore */
1478
+ ClientImpl.prototype._traceMask = function (traceObject, masked) {
1479
+ var traceObjectMasked = {};
1480
+ for (var attr in traceObject) {
1481
+ if (traceObject.hasOwnProperty(attr)) {
1482
+ if (attr == masked)
1483
+ traceObjectMasked[attr] = "******";
1484
+ else
1485
+ traceObjectMasked[attr] = traceObject[attr];
1486
+ }
1487
+ }
1488
+ return traceObjectMasked;
1489
+ };
1490
+
1491
+ // ------------------------------------------------------------------------
1492
+ // Public Programming interface.
1493
+ // ------------------------------------------------------------------------
1494
+
1495
+ /**
1496
+ * The JavaScript application communicates to the server using a {@link Paho.MQTT.Client} object.
1497
+ * <p>
1498
+ * Most applications will create just one Client object and then call its connect() method,
1499
+ * however applications can create more than one Client object if they wish.
1500
+ * In this case the combination of host, port and clientId attributes must be different for each Client object.
1501
+ * <p>
1502
+ * The send, subscribe and unsubscribe methods are implemented as asynchronous JavaScript methods
1503
+ * (even though the underlying protocol exchange might be synchronous in nature).
1504
+ * This means they signal their completion by calling back to the application,
1505
+ * via Success or Failure callback functions provided by the application on the method in question.
1506
+ * Such callbacks are called at most once per method invocation and do not persist beyond the lifetime
1507
+ * of the script that made the invocation.
1508
+ * <p>
1509
+ * In contrast there are some callback functions, most notably <i>onMessageArrived</i>,
1510
+ * that are defined on the {@link Paho.MQTT.Client} object.
1511
+ * These may get called multiple times, and aren't directly related to specific method invocations made by the client.
1512
+ *
1513
+ * @name Paho.MQTT.Client
1514
+ *
1515
+ * @constructor
1516
+ *
1517
+ * @param {string} host - the address of the messaging server, as a fully qualified WebSocket URI, as a DNS name or dotted decimal IP address.
1518
+ * @param {number} port - the port number to connect to - only required if host is not a URI
1519
+ * @param {string} path - the path on the host to connect to - only used if host is not a URI. Default: '/mqtt'.
1520
+ * @param {string} clientId - the Messaging client identifier, between 1 and 23 characters in length.
1521
+ *
1522
+ * @property {string} host - <i>read only</i> the server's DNS hostname or dotted decimal IP address.
1523
+ * @property {number} port - <i>read only</i> the server's port.
1524
+ * @property {string} path - <i>read only</i> the server's path.
1525
+ * @property {string} clientId - <i>read only</i> used when connecting to the server.
1526
+ * @property {function} onConnectionLost - called when a connection has been lost.
1527
+ * after a connect() method has succeeded.
1528
+ * Establish the call back used when a connection has been lost. The connection may be
1529
+ * lost because the client initiates a disconnect or because the server or network
1530
+ * cause the client to be disconnected. The disconnect call back may be called without
1531
+ * the connectionComplete call back being invoked if, for example the client fails to
1532
+ * connect.
1533
+ * A single response object parameter is passed to the onConnectionLost callback containing the following fields:
1534
+ * <ol>
1535
+ * <li>errorCode
1536
+ * <li>errorMessage
1537
+ * </ol>
1538
+ * @property {function} onMessageDelivered called when a message has been delivered.
1539
+ * All processing that this Client will ever do has been completed. So, for example,
1540
+ * in the case of a Qos=2 message sent by this client, the PubComp flow has been received from the server
1541
+ * and the message has been removed from persistent storage before this callback is invoked.
1542
+ * Parameters passed to the onMessageDelivered callback are:
1543
+ * <ol>
1544
+ * <li>{@link Paho.MQTT.Message} that was delivered.
1545
+ * </ol>
1546
+ * @property {function} onMessageArrived called when a message has arrived in this Paho.MQTT.client.
1547
+ * Parameters passed to the onMessageArrived callback are:
1548
+ * <ol>
1549
+ * <li>{@link Paho.MQTT.Message} that has arrived.
1550
+ * </ol>
1551
+ */
1552
+ var Client = function (host, port, path, clientId) {
1553
+
1554
+ var uri;
1555
+
1556
+ if (typeof host !== "string")
1557
+ throw new Error(format(ERROR.INVALID_TYPE, [typeof host, "host"]));
1558
+
1559
+ if (arguments.length == 2) {
1560
+ // host: must be full ws:// uri
1561
+ // port: clientId
1562
+ clientId = port;
1563
+ uri = host;
1564
+ var match = uri.match(/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/);
1565
+ if (match) {
1566
+ host = match[4]||match[2];
1567
+ port = parseInt(match[7]);
1568
+ path = match[8];
1569
+ } else {
1570
+ throw new Error(format(ERROR.INVALID_ARGUMENT,[host,"host"]));
1571
+ }
1572
+ } else {
1573
+ if (arguments.length == 3) {
1574
+ clientId = path;
1575
+ path = "/mqtt";
1576
+ }
1577
+ if (typeof port !== "number" || port < 0)
1578
+ throw new Error(format(ERROR.INVALID_TYPE, [typeof port, "port"]));
1579
+ if (typeof path !== "string")
1580
+ throw new Error(format(ERROR.INVALID_TYPE, [typeof path, "path"]));
1581
+
1582
+ var ipv6AddSBracket = (host.indexOf(":") != -1 && host.slice(0,1) != "[" && host.slice(-1) != "]");
1583
+ uri = "ws://"+(ipv6AddSBracket?"["+host+"]":host)+":"+port+path;
1584
+ }
1585
+
1586
+ var clientIdLength = 0;
1587
+ for (var i = 0; i<clientId.length; i++) {
1588
+ var charCode = clientId.charCodeAt(i);
1589
+ if (0xD800 <= charCode && charCode <= 0xDBFF) {
1590
+ i++; // Surrogate pair.
1591
+ }
1592
+ clientIdLength++;
1593
+ }
1594
+ if (typeof clientId !== "string" || clientIdLength > 65535)
1595
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [clientId, "clientId"]));
1596
+
1597
+ var client = new ClientImpl(uri, host, port, path, clientId);
1598
+ this._getHost = function() { return host; };
1599
+ this._setHost = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); };
1600
+
1601
+ this._getPort = function() { return port; };
1602
+ this._setPort = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); };
1603
+
1604
+ this._getPath = function() { return path; };
1605
+ this._setPath = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); };
1606
+
1607
+ this._getURI = function() { return uri; };
1608
+ this._setURI = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); };
1609
+
1610
+ this._getClientId = function() { return client.clientId; };
1611
+ this._setClientId = function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); };
1612
+
1613
+ this._getOnConnectionLost = function() { return client.onConnectionLost; };
1614
+ this._setOnConnectionLost = function(newOnConnectionLost) {
1615
+ if (typeof newOnConnectionLost === "function")
1616
+ client.onConnectionLost = newOnConnectionLost;
1617
+ else
1618
+ throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnectionLost, "onConnectionLost"]));
1619
+ };
1620
+
1621
+ this._getOnMessageDelivered = function() { return client.onMessageDelivered; };
1622
+ this._setOnMessageDelivered = function(newOnMessageDelivered) {
1623
+ if (typeof newOnMessageDelivered === "function")
1624
+ client.onMessageDelivered = newOnMessageDelivered;
1625
+ else
1626
+ throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageDelivered, "onMessageDelivered"]));
1627
+ };
1628
+
1629
+ this._getOnMessageArrived = function() { return client.onMessageArrived; };
1630
+ this._setOnMessageArrived = function(newOnMessageArrived) {
1631
+ if (typeof newOnMessageArrived === "function")
1632
+ client.onMessageArrived = newOnMessageArrived;
1633
+ else
1634
+ throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageArrived, "onMessageArrived"]));
1635
+ };
1636
+
1637
+ /**
1638
+ * Connect this Messaging client to its server.
1639
+ *
1640
+ * @name Paho.MQTT.Client#connect
1641
+ * @function
1642
+ * @param {Object} connectOptions - attributes used with the connection.
1643
+ * @param {number} connectOptions.timeout - If the connect has not succeeded within this
1644
+ * number of seconds, it is deemed to have failed.
1645
+ * The default is 30 seconds.
1646
+ * @param {string} connectOptions.userName - Authentication username for this connection.
1647
+ * @param {string} connectOptions.password - Authentication password for this connection.
1648
+ * @param {Paho.MQTT.Message} connectOptions.willMessage - sent by the server when the client
1649
+ * disconnects abnormally.
1650
+ * @param {Number} connectOptions.keepAliveInterval - the server disconnects this client if
1651
+ * there is no activity for this number of seconds.
1652
+ * The default value of 60 seconds is assumed if not set.
1653
+ * @param {boolean} connectOptions.cleanSession - if true(default) the client and server
1654
+ * persistent state is deleted on successful connect.
1655
+ * @param {boolean} connectOptions.useSSL - if present and true, use an SSL Websocket connection.
1656
+ * @param {object} connectOptions.invocationContext - passed to the onSuccess callback or onFailure callback.
1657
+ * @param {function} connectOptions.onSuccess - called when the connect acknowledgement
1658
+ * has been received from the server.
1659
+ * A single response object parameter is passed to the onSuccess callback containing the following fields:
1660
+ * <ol>
1661
+ * <li>invocationContext as passed in to the onSuccess method in the connectOptions.
1662
+ * </ol>
1663
+ * @config {function} [onFailure] called when the connect request has failed or timed out.
1664
+ * A single response object parameter is passed to the onFailure callback containing the following fields:
1665
+ * <ol>
1666
+ * <li>invocationContext as passed in to the onFailure method in the connectOptions.
1667
+ * <li>errorCode a number indicating the nature of the error.
1668
+ * <li>errorMessage text describing the error.
1669
+ * </ol>
1670
+ * @config {Array} [hosts] If present this contains either a set of hostnames or fully qualified
1671
+ * WebSocket URIs (ws://example.com:1883/mqtt), that are tried in order in place
1672
+ * of the host and port paramater on the construtor. The hosts are tried one at at time in order until
1673
+ * one of then succeeds.
1674
+ * @config {Array} [ports] If present the set of ports matching the hosts. If hosts contains URIs, this property
1675
+ * is not used.
1676
+ * @throws {InvalidState} if the client is not in disconnected state. The client must have received connectionLost
1677
+ * or disconnected before calling connect for a second or subsequent time.
1678
+ */
1679
+ this.connect = function (connectOptions) {
1680
+ connectOptions = connectOptions || {} ;
1681
+ validate(connectOptions, {timeout:"number",
1682
+ userName:"string",
1683
+ password:"string",
1684
+ willMessage:"object",
1685
+ keepAliveInterval:"number",
1686
+ cleanSession:"boolean",
1687
+ useSSL:"boolean",
1688
+ invocationContext:"object",
1689
+ onSuccess:"function",
1690
+ onFailure:"function",
1691
+ hosts:"object",
1692
+ ports:"object",
1693
+ mqttVersion:"number"});
1694
+
1695
+ // If no keep alive interval is set, assume 60 seconds.
1696
+ if (connectOptions.keepAliveInterval === undefined)
1697
+ connectOptions.keepAliveInterval = 60;
1698
+
1699
+ if (connectOptions.mqttVersion > 4 || connectOptions.mqttVersion < 3) {
1700
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.mqttVersion, "connectOptions.mqttVersion"]));
1701
+ }
1702
+
1703
+ if (connectOptions.mqttVersion === undefined) {
1704
+ connectOptions.mqttVersionExplicit = false;
1705
+ connectOptions.mqttVersion = 4;
1706
+ } else {
1707
+ connectOptions.mqttVersionExplicit = true;
1708
+ }
1709
+
1710
+ //Check that if password is set, so is username
1711
+ if (connectOptions.password === undefined && connectOptions.userName !== undefined)
1712
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.password, "connectOptions.password"]))
1713
+
1714
+ if (connectOptions.willMessage) {
1715
+ if (!(connectOptions.willMessage instanceof Message))
1716
+ throw new Error(format(ERROR.INVALID_TYPE, [connectOptions.willMessage, "connectOptions.willMessage"]));
1717
+ // The will message must have a payload that can be represented as a string.
1718
+ // Cause the willMessage to throw an exception if this is not the case.
1719
+ connectOptions.willMessage.stringPayload;
1720
+
1721
+ if (typeof connectOptions.willMessage.destinationName === "undefined")
1722
+ throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.willMessage.destinationName, "connectOptions.willMessage.destinationName"]));
1723
+ }
1724
+ if (typeof connectOptions.cleanSession === "undefined")
1725
+ connectOptions.cleanSession = true;
1726
+ if (connectOptions.hosts) {
1727
+
1728
+ if (!(connectOptions.hosts instanceof Array) )
1729
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"]));
1730
+ if (connectOptions.hosts.length <1 )
1731
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"]));
1732
+
1733
+ var usingURIs = false;
1734
+ for (var i = 0; i<connectOptions.hosts.length; i++) {
1735
+ if (typeof connectOptions.hosts[i] !== "string")
1736
+ throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.hosts[i], "connectOptions.hosts["+i+"]"]));
1737
+ if (/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/.test(connectOptions.hosts[i])) {
1738
+ if (i == 0) {
1739
+ usingURIs = true;
1740
+ } else if (!usingURIs) {
1741
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts[i], "connectOptions.hosts["+i+"]"]));
1742
+ }
1743
+ } else if (usingURIs) {
1744
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts[i], "connectOptions.hosts["+i+"]"]));
1745
+ }
1746
+ }
1747
+
1748
+ if (!usingURIs) {
1749
+ if (!connectOptions.ports)
1750
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"]));
1751
+ if (!(connectOptions.ports instanceof Array) )
1752
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"]));
1753
+ if (connectOptions.hosts.length != connectOptions.ports.length)
1754
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.ports, "connectOptions.ports"]));
1755
+
1756
+ connectOptions.uris = [];
1757
+
1758
+ for (var i = 0; i<connectOptions.hosts.length; i++) {
1759
+ if (typeof connectOptions.ports[i] !== "number" || connectOptions.ports[i] < 0)
1760
+ throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.ports[i], "connectOptions.ports["+i+"]"]));
1761
+ var host = connectOptions.hosts[i];
1762
+ var port = connectOptions.ports[i];
1763
+
1764
+ var ipv6 = (host.indexOf(":") != -1);
1765
+ uri = "ws://"+(ipv6?"["+host+"]":host)+":"+port+path;
1766
+ connectOptions.uris.push(uri);
1767
+ }
1768
+ } else {
1769
+ connectOptions.uris = connectOptions.hosts;
1770
+ }
1771
+ }
1772
+
1773
+ client.connect(connectOptions);
1774
+ };
1775
+
1776
+ /**
1777
+ * Subscribe for messages, request receipt of a copy of messages sent to the destinations described by the filter.
1778
+ *
1779
+ * @name Paho.MQTT.Client#subscribe
1780
+ * @function
1781
+ * @param {string} filter describing the destinations to receive messages from.
1782
+ * <br>
1783
+ * @param {object} subscribeOptions - used to control the subscription
1784
+ *
1785
+ * @param {number} subscribeOptions.qos - the maiximum qos of any publications sent
1786
+ * as a result of making this subscription.
1787
+ * @param {object} subscribeOptions.invocationContext - passed to the onSuccess callback
1788
+ * or onFailure callback.
1789
+ * @param {function} subscribeOptions.onSuccess - called when the subscribe acknowledgement
1790
+ * has been received from the server.
1791
+ * A single response object parameter is passed to the onSuccess callback containing the following fields:
1792
+ * <ol>
1793
+ * <li>invocationContext if set in the subscribeOptions.
1794
+ * </ol>
1795
+ * @param {function} subscribeOptions.onFailure - called when the subscribe request has failed or timed out.
1796
+ * A single response object parameter is passed to the onFailure callback containing the following fields:
1797
+ * <ol>
1798
+ * <li>invocationContext - if set in the subscribeOptions.
1799
+ * <li>errorCode - a number indicating the nature of the error.
1800
+ * <li>errorMessage - text describing the error.
1801
+ * </ol>
1802
+ * @param {number} subscribeOptions.timeout - which, if present, determines the number of
1803
+ * seconds after which the onFailure calback is called.
1804
+ * The presence of a timeout does not prevent the onSuccess
1805
+ * callback from being called when the subscribe completes.
1806
+ * @throws {InvalidState} if the client is not in connected state.
1807
+ */
1808
+ this.subscribe = function (filter, subscribeOptions) {
1809
+ if (typeof filter !== "string")
1810
+ throw new Error("Invalid argument:"+filter);
1811
+ subscribeOptions = subscribeOptions || {} ;
1812
+ validate(subscribeOptions, {qos:"number",
1813
+ invocationContext:"object",
1814
+ onSuccess:"function",
1815
+ onFailure:"function",
1816
+ timeout:"number"
1817
+ });
1818
+ if (subscribeOptions.timeout && !subscribeOptions.onFailure)
1819
+ throw new Error("subscribeOptions.timeout specified with no onFailure callback.");
1820
+ if (typeof subscribeOptions.qos !== "undefined"
1821
+ && !(subscribeOptions.qos === 0 || subscribeOptions.qos === 1 || subscribeOptions.qos === 2 ))
1822
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [subscribeOptions.qos, "subscribeOptions.qos"]));
1823
+ client.subscribe(filter, subscribeOptions);
1824
+ };
1825
+
1826
+ /**
1827
+ * Unsubscribe for messages, stop receiving messages sent to destinations described by the filter.
1828
+ *
1829
+ * @name Paho.MQTT.Client#unsubscribe
1830
+ * @function
1831
+ * @param {string} filter - describing the destinations to receive messages from.
1832
+ * @param {object} unsubscribeOptions - used to control the subscription
1833
+ * @param {object} unsubscribeOptions.invocationContext - passed to the onSuccess callback
1834
+ or onFailure callback.
1835
+ * @param {function} unsubscribeOptions.onSuccess - called when the unsubscribe acknowledgement has been received from the server.
1836
+ * A single response object parameter is passed to the
1837
+ * onSuccess callback containing the following fields:
1838
+ * <ol>
1839
+ * <li>invocationContext - if set in the unsubscribeOptions.
1840
+ * </ol>
1841
+ * @param {function} unsubscribeOptions.onFailure called when the unsubscribe request has failed or timed out.
1842
+ * A single response object parameter is passed to the onFailure callback containing the following fields:
1843
+ * <ol>
1844
+ * <li>invocationContext - if set in the unsubscribeOptions.
1845
+ * <li>errorCode - a number indicating the nature of the error.
1846
+ * <li>errorMessage - text describing the error.
1847
+ * </ol>
1848
+ * @param {number} unsubscribeOptions.timeout - which, if present, determines the number of seconds
1849
+ * after which the onFailure callback is called. The presence of
1850
+ * a timeout does not prevent the onSuccess callback from being
1851
+ * called when the unsubscribe completes
1852
+ * @throws {InvalidState} if the client is not in connected state.
1853
+ */
1854
+ this.unsubscribe = function (filter, unsubscribeOptions) {
1855
+ if (typeof filter !== "string")
1856
+ throw new Error("Invalid argument:"+filter);
1857
+ unsubscribeOptions = unsubscribeOptions || {} ;
1858
+ validate(unsubscribeOptions, {invocationContext:"object",
1859
+ onSuccess:"function",
1860
+ onFailure:"function",
1861
+ timeout:"number"
1862
+ });
1863
+ if (unsubscribeOptions.timeout && !unsubscribeOptions.onFailure)
1864
+ throw new Error("unsubscribeOptions.timeout specified with no onFailure callback.");
1865
+ client.unsubscribe(filter, unsubscribeOptions);
1866
+ };
1867
+
1868
+ /**
1869
+ * Send a message to the consumers of the destination in the Message.
1870
+ *
1871
+ * @name Paho.MQTT.Client#send
1872
+ * @function
1873
+ * @param {Paho.MQTT.Message} message to send.
1874
+
1875
+ * @throws {InvalidState} if the client is not connected.
1876
+ */
1877
+ this.send = function (message) {
1878
+ if (!(message instanceof Message))
1879
+ throw new Error("Invalid argument:"+typeof message);
1880
+ if (typeof message.destinationName === "undefined")
1881
+ throw new Error("Invalid parameter Message.destinationName:"+message.destinationName);
1882
+
1883
+ client.send(message);
1884
+ };
1885
+
1886
+ /**
1887
+ * Normal disconnect of this Messaging client from its server.
1888
+ *
1889
+ * @name Paho.MQTT.Client#disconnect
1890
+ * @function
1891
+ * @throws {InvalidState} if the client is already disconnected.
1892
+ */
1893
+ this.disconnect = function () {
1894
+ client.disconnect();
1895
+ };
1896
+
1897
+ /**
1898
+ * Get the contents of the trace log.
1899
+ *
1900
+ * @name Paho.MQTT.Client#getTraceLog
1901
+ * @function
1902
+ * @return {Object[]} tracebuffer containing the time ordered trace records.
1903
+ */
1904
+ this.getTraceLog = function () {
1905
+ return client.getTraceLog();
1906
+ }
1907
+
1908
+ /**
1909
+ * Start tracing.
1910
+ *
1911
+ * @name Paho.MQTT.Client#startTrace
1912
+ * @function
1913
+ */
1914
+ this.startTrace = function () {
1915
+ client.startTrace();
1916
+ };
1917
+
1918
+ /**
1919
+ * Stop tracing.
1920
+ *
1921
+ * @name Paho.MQTT.Client#stopTrace
1922
+ * @function
1923
+ */
1924
+ this.stopTrace = function () {
1925
+ client.stopTrace();
1926
+ };
1927
+
1928
+ this.isConnected = function() {
1929
+ return client.connected;
1930
+ };
1931
+ };
1932
+
1933
+ Client.prototype = {
1934
+ get host() { return this._getHost(); },
1935
+ set host(newHost) { this._setHost(newHost); },
1936
+
1937
+ get port() { return this._getPort(); },
1938
+ set port(newPort) { this._setPort(newPort); },
1939
+
1940
+ get path() { return this._getPath(); },
1941
+ set path(newPath) { this._setPath(newPath); },
1942
+
1943
+ get clientId() { return this._getClientId(); },
1944
+ set clientId(newClientId) { this._setClientId(newClientId); },
1945
+
1946
+ get onConnectionLost() { return this._getOnConnectionLost(); },
1947
+ set onConnectionLost(newOnConnectionLost) { this._setOnConnectionLost(newOnConnectionLost); },
1948
+
1949
+ get onMessageDelivered() { return this._getOnMessageDelivered(); },
1950
+ set onMessageDelivered(newOnMessageDelivered) { this._setOnMessageDelivered(newOnMessageDelivered); },
1951
+
1952
+ get onMessageArrived() { return this._getOnMessageArrived(); },
1953
+ set onMessageArrived(newOnMessageArrived) { this._setOnMessageArrived(newOnMessageArrived); }
1954
+ };
1955
+
1956
+ /**
1957
+ * An application message, sent or received.
1958
+ * <p>
1959
+ * All attributes may be null, which implies the default values.
1960
+ *
1961
+ * @name Paho.MQTT.Message
1962
+ * @constructor
1963
+ * @param {String|ArrayBuffer} payload The message data to be sent.
1964
+ * <p>
1965
+ * @property {string} payloadString <i>read only</i> The payload as a string if the payload consists of valid UTF-8 characters.
1966
+ * @property {ArrayBuffer} payloadBytes <i>read only</i> The payload as an ArrayBuffer.
1967
+ * <p>
1968
+ * @property {string} destinationName <b>mandatory</b> The name of the destination to which the message is to be sent
1969
+ * (for messages about to be sent) or the name of the destination from which the message has been received.
1970
+ * (for messages received by the onMessage function).
1971
+ * <p>
1972
+ * @property {number} qos The Quality of Service used to deliver the message.
1973
+ * <dl>
1974
+ * <dt>0 Best effort (default).
1975
+ * <dt>1 At least once.
1976
+ * <dt>2 Exactly once.
1977
+ * </dl>
1978
+ * <p>
1979
+ * @property {Boolean} retained If true, the message is to be retained by the server and delivered
1980
+ * to both current and future subscriptions.
1981
+ * If false the server only delivers the message to current subscribers, this is the default for new Messages.
1982
+ * A received message has the retained boolean set to true if the message was published
1983
+ * with the retained boolean set to true
1984
+ * and the subscrption was made after the message has been published.
1985
+ * <p>
1986
+ * @property {Boolean} duplicate <i>read only</i> If true, this message might be a duplicate of one which has already been received.
1987
+ * This is only set on messages received from the server.
1988
+ *
1989
+ */
1990
+ var Message = function (newPayload) {
1991
+ var payload;
1992
+ if ( typeof newPayload === "string"
1993
+ || newPayload instanceof ArrayBuffer
1994
+ || newPayload instanceof Int8Array
1995
+ || newPayload instanceof Uint8Array
1996
+ || newPayload instanceof Int16Array
1997
+ || newPayload instanceof Uint16Array
1998
+ || newPayload instanceof Int32Array
1999
+ || newPayload instanceof Uint32Array
2000
+ || newPayload instanceof Float32Array
2001
+ || newPayload instanceof Float64Array
2002
+ ) {
2003
+ payload = newPayload;
2004
+ } else {
2005
+ throw (format(ERROR.INVALID_ARGUMENT, [newPayload, "newPayload"]));
2006
+ }
2007
+
2008
+ this._getPayloadString = function () {
2009
+ if (typeof payload === "string")
2010
+ return payload;
2011
+ else
2012
+ return parseUTF8(payload, 0, payload.length);
2013
+ };
2014
+
2015
+ this._getPayloadBytes = function() {
2016
+ if (typeof payload === "string") {
2017
+ var buffer = new ArrayBuffer(UTF8Length(payload));
2018
+ var byteStream = new Uint8Array(buffer);
2019
+ stringToUTF8(payload, byteStream, 0);
2020
+
2021
+ return byteStream;
2022
+ } else {
2023
+ return payload;
2024
+ };
2025
+ };
2026
+
2027
+ var destinationName = undefined;
2028
+ this._getDestinationName = function() { return destinationName; };
2029
+ this._setDestinationName = function(newDestinationName) {
2030
+ if (typeof newDestinationName === "string")
2031
+ destinationName = newDestinationName;
2032
+ else
2033
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [newDestinationName, "newDestinationName"]));
2034
+ };
2035
+
2036
+ var qos = 0;
2037
+ this._getQos = function() { return qos; };
2038
+ this._setQos = function(newQos) {
2039
+ if (newQos === 0 || newQos === 1 || newQos === 2 )
2040
+ qos = newQos;
2041
+ else
2042
+ throw new Error("Invalid argument:"+newQos);
2043
+ };
2044
+
2045
+ var retained = false;
2046
+ this._getRetained = function() { return retained; };
2047
+ this._setRetained = function(newRetained) {
2048
+ if (typeof newRetained === "boolean")
2049
+ retained = newRetained;
2050
+ else
2051
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [newRetained, "newRetained"]));
2052
+ };
2053
+
2054
+ var duplicate = false;
2055
+ this._getDuplicate = function() { return duplicate; };
2056
+ this._setDuplicate = function(newDuplicate) { duplicate = newDuplicate; };
2057
+ };
2058
+
2059
+ Message.prototype = {
2060
+ get payloadString() { return this._getPayloadString(); },
2061
+ get payloadBytes() { return this._getPayloadBytes(); },
2062
+
2063
+ get destinationName() { return this._getDestinationName(); },
2064
+ set destinationName(newDestinationName) { this._setDestinationName(newDestinationName); },
2065
+
2066
+ get qos() { return this._getQos(); },
2067
+ set qos(newQos) { this._setQos(newQos); },
2068
+
2069
+ get retained() { return this._getRetained(); },
2070
+ set retained(newRetained) { this._setRetained(newRetained); },
2071
+
2072
+ get duplicate() { return this._getDuplicate(); },
2073
+ set duplicate(newDuplicate) { this._setDuplicate(newDuplicate); }
2074
+ };
2075
+
2076
+ // Module contents.
2077
+ return {
2078
+ Client: Client,
2079
+ Message: Message
2080
+ };
2081
+ })(window);