17. Chat sencillo - Protocolos de comunicación

Versión para imprimir.

A. Introducción

B. Diagrama de despliegue

Diagrama de despliegue

C. Hazlo funcionar

  1. Revisa el proyecto en Replit con la URL https://replit.com/@GilbertoPachec5/chat?v=1. Hazle fork al proyecto y córrelo. En el ambiente de desarrollo tienes la opción de descargar el proyecto en un zip.

  2. Copia la url de la app y pégala en varias pestañas, navegadores y dispositivos para que veas como entre todas estas vistas se puede chatear.

  3. Este proyecto puede correr simultáneamente en varios navegadores y computadoras. Todos interactuan con el servidor test.mosquitto.org.

  4. Usa o crea una cuenta de Google.

  5. Crea una cuenta de Replit usando la cuenta de Google.

  6. Crea un proyecto con la categoría HTML, CSS, JS en Replit y edita o sube los archivos de este proyecto.

  7. Depura el proyecto.

  8. Crea la cover page o página de spotlight del proyecto.

  9. El proyecto ya contiene la carpeta paho.javascript-1.0, pero se puede descargar de https://eclipse.dev/paho/index.php?page=clients/js/index.php

D. Archivos

Haz clic en los triángulos para expandir las carpetas

E. index.html

1<!DOCTYPE html>
2<html>
3
4<head>
5
6 <meta charset="UTF-8">
7 <meta name="viewport" content="width=device-width">
8
9 <title>Chat</title>
10
11 <script src="paho.javascript-1.0.3/paho-mqtt-min.js"></script>
12
13</head>
14
15<body>
16
17 <form onsubmit="formActivada(event)">
18
19 <h1>Chat</h1>
20
21 <p>
22 <label>
23 Alias *
24 <input id="inputAlias" required>
25 </label>
26 </p>
27
28 <p>
29 <label>
30 Mensaje *
31 <input id="inputMensaje" required>
32 </label>
33 </p>
34
35 <p>* Obligatorio</p>
36
37 <p><button type="submit">Enviar</button></p>
38
39 <pre id="pre"></pre>
40
41 </form>
42
43 <script type="module">
44
45 import { creaClientIdMqtt } from "./lib/js/creaClientIdMqtt.js"
46 import { falloEnLaConexionMqtt } from "./lib/js/falloEnLaConexionMqtt.js"
47 import { conexionMqttPerdida } from "./lib/js/conexionMqttPerdida.js"
48 import { muestraError } from "./lib/js/muestraError.js"
49
50 const TOPICO_CHAT = "gilpgawoas/chat"
51
52 // Cambia por una raíz para tu proyecto.
53 const clientId = creaClientIdMqtt("gilpgawoasChat-")
54
55 // Si usas un servidor de MQTT diferente, necesitas cambiar los parámetros.
56 const cliente = new Paho.MQTT.Client("test.mosquitto.org", 8081, clientId)
57
58 /**
59 * @param {Event} event
60 */
61 function formActivada(event) {
62 try {
63 event.preventDefault()
64 const mensaje = `${inputAlias.value.trim()}
65${inputMensaje.value.trim()}`
66 enviaMensajeMqtt(mensaje, TOPICO_CHAT)
67 } catch (error) {
68 muestraError(error)
69 }
70 }
71 // Permite que los eventos de html usen la función.
72 window["formActivada"] = formActivada
73
74 // Acciones al recibir un mensaje.
75 cliente.onMessageArrived = mensaje => {
76 if (mensaje.destinationName === TOPICO_CHAT) {
77 pre.textContent += mensaje.payloadString + "\n\n"
78 }
79 }
80
81 // Acciones al perder la conexión.
82 cliente.onConnectionLost = conexionMqttPerdida
83
84 // Configura el cliente.
85 cliente.connect({
86
87 keepAliveInterval: 10,
88
89 useSSL: true,
90
91 // Acciones al fallar la conexión.
92 onFailure: falloEnLaConexionMqtt,
93
94 // Acciones al lograr la conexión.
95 onSuccess: () => {
96 console.log("Conectado")
97 // Se suscribe a uno o más tópicos.
98 cliente.subscribe(TOPICO_CHAT)
99 },
100
101 })
102
103 /**
104 * Envá un valor al servidor de MQTT y es reenviado a todos los dispositivos
105 * suscritos al tópico indicado
106 * @param {string} mensaje
107 * @param {string} topico
108 */
109 function enviaMensajeMqtt(mensaje, topico) {
110 const mensajeMqtt = new Paho.MQTT.Message(mensaje)
111 mensajeMqtt.destinationName = topico
112 cliente.send(mensajeMqtt)
113 }
114
115 </script>
116
117</body>
118
119</html>

F. Carpeta « lib »

Versión para imprimir.

A. Carpeta « lib / js »

1. lib / js / conexionMqttPerdida.js

1/**
2 * @param { {
3 * errorCode: number,
4 * errorMessage: string
5 * } } responseObject
6 */
7export function conexionMqttPerdida(responseObject) {
8 if (responseObject.errorCode !== 0) {
9 const mensaje = "Conexión terminada " + responseObject.errorMessage
10 console.error(mensaje)
11 alert(mensaje)
12 }
13}
14
15// Permite que los eventos de html usen la función.
16window["conexionMqttPerdida"] = conexionMqttPerdida

2. lib / js / creaClientIdMqtt.js

1/**
2 * Añade caracteres al azar a una raíz, para obtener un clientId único por cada
3 * instancia que se conecte al servidor de mqtt.
4 * @param {string} raiz
5 */
6export function creaClientIdMqtt(raiz) {
7 const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
8 for (var i = 0; i < 15; i++) {
9 raiz += chars.charAt(Math.floor(Math.random() * chars.length))
10 }
11 return raiz
12}
13
14// Permite que los eventos de html usen la función.
15window["creaClientIdMqtt"] = creaClientIdMqtt

3. lib / js / falloEnLaConexionMqtt.js

1/**
2 * @param { { errorMessage: string } } res
3 */
4export function falloEnLaConexionMqtt(res) {
5 const mensaje = "Fallo en conexión:" + res.errorMessage
6 console.error(mensaje)
7 alert(mensaje)
8}
9
10// Permite que los eventos de html usen la función.
11window["falloEnLaConexionMqtt"] = falloEnLaConexionMqtt

4. lib / js / muestraError.js

1import { ProblemDetails } from "./ProblemDetails.js"
2
3/**
4 * Muestra un error en la consola y en un cuadro de
5 * alerta el mensaje de una excepción.
6 * @param { ProblemDetails | Error | null } error descripción del error.
7 */
8export function muestraError(error) {
9 if (error === null) {
10 console.log("Error")
11 alert("Error")
12 } else if (error instanceof ProblemDetails) {
13 let mensaje = error.title
14 if (error.detail) {
15 mensaje += `\n\n${error.detail}`
16 }
17 mensaje += `\n\nCódigo: ${error.status}`
18 if (error.type) {
19 mensaje += ` ${error.type}`
20 }
21 console.error(mensaje)
22 console.error(error)
23 alert(mensaje)
24 } else {
25 console.error(error)
26 alert(error.message)
27 }
28}
29
30// Permite que los eventos de html usen la función.
31window["muestraError"] = muestraError

5. lib / js / ProblemDetails.js

1export const ProblemDetails_BadRequest = 400
2export const ProblemDetails_NotFound = 404
3export const ProblemDetails_InternalServerError = 500
4
5export class ProblemDetails extends Error {
6
7 /**
8 * @param {number} status
9 * @param {string} title
10 * @param {string} [detail]
11 * @param {string} [type]
12 */
13 constructor(status, title, detail, type) {
14 super(title)
15 /** @readonly */
16 this.status = status
17 /** @readonly */
18 this.type = type
19 /** @readonly */
20 this.title = title
21 /** @readonly */
22 this.detail = detail
23 }
24
25}

G. jsconfig.json

1{
2 "compilerOptions": {
3 "checkJs": true,
4 "strictNullChecks": true,
5 "target": "ES6",
6 "module": "ES6",
7 "moduleResolution": "classic",
8 "lib": [
9 "ES2017",
10 "WebWorker",
11 "DOM"
12 ]
13 }
14}

H. Carpeta « paho.javascript-1.0.3 »

Versión para imprimir.

A. paho.javascript-1.0.3 / about.html

1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2<html xmlns="http://www.w3.org/1999/xhtml"><head>
3<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
4<title>About</title>
5</head>
6<body lang="EN-US">
7<h2>About This Content</h2>
8
9<p><em>December 9, 2013</em></p>
10<h3>License</h3>
11
12<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
13indicated below, the Content is provided to you under the terms and conditions of the
14Eclipse Public License Version 1.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL").
15A copy of the EPL is available at
16<a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
17and a copy of the EDL is available at
18<a href="http://www.eclipse.org/org/documents/edl-v10.php">http://www.eclipse.org/org/documents/edl-v10.php</a>.
19For purposes of the EPL, "Program" will mean the Content.</p>
20
21<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
22being redistributed by another party ("Redistributor") and different terms and conditions may
23apply to your use of any object code in the Content. Check the Redistributor's license that was
24provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
25indicated below, the terms and conditions of the EPL still apply to any source code in the Content
26and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
27
28</body></html>
29

B. paho.javascript-1.0.3 / CONTRIBUTING.md

1# Contributing to Paho
2
3Thanks for your interest in this project!
4
5You can contribute bugfixes and new features by sending pull requests through GitHub.
6
7## Legal
8
9In order for your contribution to be accepted, it must comply with the Eclipse Foundation IP policy.
10
11Please read the [Eclipse Foundation policy on accepting contributions via Git](http://wiki.eclipse.org/Development_Resources/Contributing_via_Git).
12
131. Sign the [Eclipse CLA](http://www.eclipse.org/legal/CLA.php)
14 1. Register for an Eclipse Foundation User ID. You can register [here](https://dev.eclipse.org/site_login/createaccount.php).
15 2. Log into the [Projects Portal](https://projects.eclipse.org/), and click on the '[Eclipse CLA](https://projects.eclipse.org/user/sign/cla)' link.
162. Go to your [account settings](https://dev.eclipse.org/site_login/myaccount.php#open_tab_accountsettings) and add your GitHub username to your account.
173. Make sure that you _sign-off_ your Git commits in the following format:
18 ``` Signed-off-by: John Smith ``` This is usually at the bottom of the commit message. You can automate this by adding the '-s' flag when you make the commits. e.g. ```git commit -s -m "Adding a cool feature"```
194. Ensure that the email address that you make your commits with is the same one you used to sign up to the Eclipse Foundation website with.
20
21## Contributing a change
22
23## Contributing a change
24
251. [Fork the repository on GitHub](https://github.com/eclipse/paho.mqtt.javascript/fork)
262. Clone the forked repository onto your computer: ``` git clone https://github.com//paho.mqtt.javascript.git ```
273. Create a new branch from the latest ```develop``` branch with ```git checkout -b YOUR_BRANCH_NAME origin/develop```
284. Make your changes
295. If developing a new feature, make sure to include JUnit tests.
306. Ensure that all new and existing tests pass.
317. Commit the changes into the branch: ``` git commit -s ``` Make sure that your commit message is meaningful and describes your changes correctly.
328. If you have a lot of commits for the change, squash them into a single / few commits.
339. Push the changes in your branch to your forked repository.
3410. Finally, go to [https://github.com/eclipse/paho.mqtt.javascript](https://github.com/eclipse/paho.mqtt.javascript) and create a pull request from your "YOUR_BRANCH_NAME" branch to the ```develop``` one to request review and merge of the commits in your pushed branch.
35
36
37What happens next depends on the content of the patch. If it is 100% authored
38by the contributor and is less than 1000 lines (and meets the needs of the
39project), then it can be pulled into the main repository. If not, more steps
40are required. These are detailed in the
41[legal process poster](http://www.eclipse.org/legal/EclipseLegalProcessPoster.pdf).
42
43
44
45## Developer resources:
46
47
48Information regarding source code management, builds, coding standards, and more.
49
50- [https://projects.eclipse.org/projects/iot.paho/developer](https://projects.eclipse.org/projects/iot.paho/developer)
51
52Contact:
53--------
54
55Contact the project developers via the project's development
56[mailing list](https://dev.eclipse.org/mailman/listinfo/paho-dev).
57
58Search for bugs:
59----------------
60
61This project uses GitHub Issues here: [github.com/eclipse/paho.mqtt.javascript/issues](https://github.com/eclipse/paho.mqtt.javascript/issues) to track ongoing development and issues.
62
63Create a new bug:
64-----------------
65
66Be sure to search for existing bugs before you create another one. Remember that contributions are always welcome!
67
68- [Create new Paho bug](https://github.com/eclipse/paho.mqtt.javascript/issues/new)
69

C. paho.javascript-1.0.3 / edl-v10

1
2Eclipse Distribution License - v 1.0
3
4Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
5
6All rights reserved.
7
8Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
9
10 Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
12 Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
13
14THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
16

D. paho.javascript-1.0.3 / epl-v10

1Eclipse Public License - v 1.0
2
3THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
4
51. DEFINITIONS
6
7"Contribution" means:
8
9a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
10b) in the case of each subsequent Contributor:
11i) changes to the Program, and
12ii) additions to the Program;
13where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
14"Contributor" means any person or entity that distributes the Program.
15
16"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
17
18"Program" means the Contributions distributed in accordance with this Agreement.
19
20"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
21
222. GRANT OF RIGHTS
23
24a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
25b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
26c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
27d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
283. REQUIREMENTS
29
30A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
31
32a) it complies with the terms and conditions of this Agreement; and
33b) its license agreement:
34i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
35ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
36iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
37iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
38When the Program is made available in source code form:
39
40a) it must be made available under this Agreement; and
41b) a copy of this Agreement must be included with each copy of the Program.
42Contributors may not remove or alter any copyright notices contained within the Program.
43
44Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
45
464. COMMERCIAL DISTRIBUTION
47
48Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
49
50For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
51
525. NO WARRANTY
53
54EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
55
566. DISCLAIMER OF LIABILITY
57
58EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
59
607. GENERAL
61
62If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
63
64If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
65
66All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
67
68Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
69
70This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
71

E. paho.javascript-1.0.3 / paho-mqtt-min.js

1/*******************************************************************************
2 * Copyright (c) 2013, 2016 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 *******************************************************************************/
14(function(p,s){"object"===typeof exports&&"object"===typeof module?module.exports=s():"function"===typeof define&&define.amd?define(s):"object"===typeof exports?exports=s():("undefined"===typeof p.Paho&&(p.Paho={}),p.Paho.MQTT=s())})(this,function(){return function(p){function s(a,b,c){b[c++]=a>>8;b[c++]=a%256;return c}function u(a,b,c,k){k=s(b,c,k);D(a,c,k);return k+b}function n(a){for(var b=0,c=0;c<a.length;c++){var k=a.charCodeAt(c);2047<k?(55296<=k&&56319>=k&&(c++,b++),b+=3):127<k?b+=2:b++}return b}
15function D(a,b,c){for(var k=0;k<a.length;k++){var e=a.charCodeAt(k);if(55296<=e&&56319>=e){var g=a.charCodeAt(++k);if(isNaN(g))throw Error(f(h.MALFORMED_UNICODE,[e,g]));e=(e-55296<<10)+(g-56320)+65536}127>=e?b[c++]=e:(2047>=e?b[c++]=e>>6&31|192:(65535>=e?b[c++]=e>>12&15|224:(b[c++]=e>>18&7|240,b[c++]=e>>12&63|128),b[c++]=e>>6&63|128),b[c++]=e&63|128)}return b}function E(a,b,c){for(var k="",e,g=b;g<b+c;){e=a[g++];if(!(128>e)){var m=a[g++]-128;if(0>m)throw Error(f(h.MALFORMED_UTF,[e.toString(16),m.toString(16),
16""]));if(224>e)e=64*(e-192)+m;else{var d=a[g++]-128;if(0>d)throw Error(f(h.MALFORMED_UTF,[e.toString(16),m.toString(16),d.toString(16)]));if(240>e)e=4096*(e-224)+64*m+d;else{var l=a[g++]-128;if(0>l)throw Error(f(h.MALFORMED_UTF,[e.toString(16),m.toString(16),d.toString(16),l.toString(16)]));if(248>e)e=262144*(e-240)+4096*m+64*d+l;else throw Error(f(h.MALFORMED_UTF,[e.toString(16),m.toString(16),d.toString(16),l.toString(16)]));}}}65535<e&&(e-=65536,k+=String.fromCharCode(55296+(e>>10)),e=56320+(e&
171023));k+=String.fromCharCode(e)}return k}var z=function(a,b){for(var c in a)if(a.hasOwnProperty(c))if(b.hasOwnProperty(c)){if(typeof a[c]!==b[c])throw Error(f(h.INVALID_TYPE,[typeof a[c],c]));}else{c="Unknown property, "+c+". Valid properties are:";for(var k in b)b.hasOwnProperty(k)&&(c=c+" "+k);throw Error(c);}},v=function(a,b){return function(){return a.apply(b,arguments)}},h={OK:{code:0,text:"AMQJSC0000I OK."},CONNECT_TIMEOUT:{code:1,text:"AMQJSC0001E Connect timed out."},SUBSCRIBE_TIMEOUT:{code:2,
18text:"AMQJS0002E Subscribe timed out."},UNSUBSCRIBE_TIMEOUT:{code:3,text:"AMQJS0003E Unsubscribe timed out."},PING_TIMEOUT:{code:4,text:"AMQJS0004E Ping timed out."},INTERNAL_ERROR:{code:5,text:"AMQJS0005E Internal error. Error Message: {0}, Stack trace: {1}"},CONNACK_RETURNCODE:{code:6,text:"AMQJS0006E Bad Connack return code:{0} {1}."},SOCKET_ERROR:{code:7,text:"AMQJS0007E Socket error:{0}."},SOCKET_CLOSE:{code:8,text:"AMQJS0008I Socket closed."},MALFORMED_UTF:{code:9,text:"AMQJS0009E Malformed UTF data:{0} {1} {2}."},
19UNSUPPORTED:{code:10,text:"AMQJS0010E {0} is not supported by this browser."},INVALID_STATE:{code:11,text:"AMQJS0011E Invalid state {0}."},INVALID_TYPE:{code:12,text:"AMQJS0012E Invalid type {0} for {1}."},INVALID_ARGUMENT:{code:13,text:"AMQJS0013E Invalid argument {0} for {1}."},UNSUPPORTED_OPERATION:{code:14,text:"AMQJS0014E Unsupported operation."},INVALID_STORED_DATA:{code:15,text:"AMQJS0015E Invalid data in local storage key\x3d{0} value\x3d{1}."},INVALID_MQTT_MESSAGE_TYPE:{code:16,text:"AMQJS0016E Invalid MQTT message type {0}."},
20MALFORMED_UNICODE:{code:17,text:"AMQJS0017E Malformed Unicode string:{0} {1}."},BUFFER_FULL:{code:18,text:"AMQJS0018E Message buffer is full, maximum buffer size: {0}."}},H={0:"Connection Accepted",1:"Connection Refused: unacceptable protocol version",2:"Connection Refused: identifier rejected",3:"Connection Refused: server unavailable",4:"Connection Refused: bad user name or password",5:"Connection Refused: not authorized"},f=function(a,b){var c=a.text;if(b)for(var k,e,g=0;g<b.length;g++)if(k="{"+
21g+"}",e=c.indexOf(k),0<e)var h=c.substring(0,e),c=c.substring(e+k.length),c=h+b[g]+c;return c},A=[0,6,77,81,73,115,100,112,3],B=[0,4,77,81,84,84,4],q=function(a,b){this.type=a;for(var c in b)b.hasOwnProperty(c)&&(this[c]=b[c])};q.prototype.encode=function(){var a=(this.type&15)<<4,b=0,c=[],k=0,e;void 0!==this.messageIdentifier&&(b+=2);switch(this.type){case 1:switch(this.mqttVersion){case 3:b+=A.length+3;break;case 4:b+=B.length+3}b+=n(this.clientId)+2;void 0!==this.willMessage&&(b+=n(this.willMessage.destinationName)+
222,e=this.willMessage.payloadBytes,e instanceof Uint8Array||(e=new Uint8Array(h)),b+=e.byteLength+2);void 0!==this.userName&&(b+=n(this.userName)+2);void 0!==this.password&&(b+=n(this.password)+2);break;case 8:for(var a=a|2,g=0;g<this.topics.length;g++)c[g]=n(this.topics[g]),b+=c[g]+2;b+=this.requestedQos.length;break;case 10:a|=2;for(g=0;g<this.topics.length;g++)c[g]=n(this.topics[g]),b+=c[g]+2;break;case 6:a|=2;break;case 3:this.payloadMessage.duplicate&&(a|=8);a=a|=this.payloadMessage.qos<<1;this.payloadMessage.retained&&
23(a|=1);var k=n(this.payloadMessage.destinationName),h=this.payloadMessage.payloadBytes,b=b+(k+2)+h.byteLength;h instanceof ArrayBuffer?h=new Uint8Array(h):h instanceof Uint8Array||(h=new Uint8Array(h.buffer))}var f=b,g=Array(1),d=0;do{var t=f%128,f=f>>7;0<f&&(t|=128);g[d++]=t}while(0<f&&4>d);f=g.length+1;b=new ArrayBuffer(b+f);d=new Uint8Array(b);d[0]=a;d.set(g,1);if(3==this.type)f=u(this.payloadMessage.destinationName,k,d,f);else if(1==this.type){switch(this.mqttVersion){case 3:d.set(A,f);f+=A.length;
24break;case 4:d.set(B,f),f+=B.length}a=0;this.cleanSession&&(a=2);void 0!==this.willMessage&&(a=a|4|this.willMessage.qos<<3,this.willMessage.retained&&(a|=32));void 0!==this.userName&&(a|=128);void 0!==this.password&&(a|=64);d[f++]=a;f=s(this.keepAliveInterval,d,f)}void 0!==this.messageIdentifier&&(f=s(this.messageIdentifier,d,f));switch(this.type){case 1:f=u(this.clientId,n(this.clientId),d,f);void 0!==this.willMessage&&(f=u(this.willMessage.destinationName,n(this.willMessage.destinationName),d,f),
25f=s(e.byteLength,d,f),d.set(e,f),f+=e.byteLength);void 0!==this.userName&&(f=u(this.userName,n(this.userName),d,f));void 0!==this.password&&u(this.password,n(this.password),d,f);break;case 3:d.set(h,f);break;case 8:for(g=0;g<this.topics.length;g++)f=u(this.topics[g],c[g],d,f),d[f++]=this.requestedQos[g];break;case 10:for(g=0;g<this.topics.length;g++)f=u(this.topics[g],c[g],d,f)}return b};var F=function(a,b,c){this._client=a;this._window=b;this._keepAliveInterval=1E3*c;this.isReset=!1;var k=(new q(12)).encode(),
26e=function(a){return function(){return g.apply(a)}},g=function(){this.isReset?(this.isReset=!1,this._client._trace("Pinger.doPing","send PINGREQ"),this._client.socket.send(k),this.timeout=this._window.setTimeout(e(this),this._keepAliveInterval)):(this._client._trace("Pinger.doPing","Timed out"),this._client._disconnected(h.PING_TIMEOUT.code,f(h.PING_TIMEOUT)))};this.reset=function(){this.isReset=!0;this._window.clearTimeout(this.timeout);0<this._keepAliveInterval&&(this.timeout=setTimeout(e(this),
27this._keepAliveInterval))};this.cancel=function(){this._window.clearTimeout(this.timeout)}},w=function(a,b,c,f,e){this._window=b;c||(c=30);this.timeout=setTimeout(function(a,b,c){return function(){return a.apply(b,c)}}(f,a,e),1E3*c);this.cancel=function(){this._window.clearTimeout(this.timeout)}},d=function(a,b,c,d,e){if(!("WebSocket"in p&&null!==p.WebSocket))throw Error(f(h.UNSUPPORTED,["WebSocket"]));if(!("localStorage"in p&&null!==p.localStorage))throw Error(f(h.UNSUPPORTED,["localStorage"]));
28if(!("ArrayBuffer"in p&&null!==p.ArrayBuffer))throw Error(f(h.UNSUPPORTED,["ArrayBuffer"]));this._trace("Paho.MQTT.Client",a,b,c,d,e);this.host=b;this.port=c;this.path=d;this.uri=a;this.clientId=e;this._wsuri=null;this._localKey=b+":"+c+("/mqtt"!=d?":"+d:"")+":"+e+":";this._msg_queue=[];this._buffered_msg_queue=[];this._sentMessages={};this._receivedMessages={};this._notify_msg_sent={};this._message_identifier=1;this._sequence=0;for(var g in localStorage)0!==g.indexOf("Sent:"+this._localKey)&&0!==
29g.indexOf("Received:"+this._localKey)||this.restore(g)};d.prototype.host=null;d.prototype.port=null;d.prototype.path=null;d.prototype.uri=null;d.prototype.clientId=null;d.prototype.socket=null;d.prototype.connected=!1;d.prototype.maxMessageIdentifier=65536;d.prototype.connectOptions=null;d.prototype.hostIndex=null;d.prototype.onConnected=null;d.prototype.onConnectionLost=null;d.prototype.onMessageDelivered=null;d.prototype.onMessageArrived=null;d.prototype.traceFunction=null;d.prototype._msg_queue=
30null;d.prototype._buffered_msg_queue=null;d.prototype._connectTimeout=null;d.prototype.sendPinger=null;d.prototype.receivePinger=null;d.prototype._reconnectInterval=1;d.prototype._reconnecting=!1;d.prototype._reconnectTimeout=null;d.prototype.disconnectedPublishing=!1;d.prototype.disconnectedBufferSize=5E3;d.prototype.receiveBuffer=null;d.prototype._traceBuffer=null;d.prototype._MAX_TRACE_ENTRIES=100;d.prototype.connect=function(a){var b=this._traceMask(a,"password");this._trace("Client.connect",
31b,this.socket,this.connected);if(this.connected)throw Error(f(h.INVALID_STATE,["already connected"]));if(this.socket)throw Error(f(h.INVALID_STATE,["already connected"]));this._reconnecting&&(this._reconnectTimeout.cancel(),this._reconnectTimeout=null,this._reconnecting=!1);this.connectOptions=a;this._reconnectInterval=1;this._reconnecting=!1;a.uris?(this.hostIndex=0,this._doConnect(a.uris[0])):this._doConnect(this.uri)};d.prototype.subscribe=function(a,b){this._trace("Client.subscribe",a,b);if(!this.connected)throw Error(f(h.INVALID_STATE,
32["not connected"]));var c=new q(8);c.topics=[a];c.requestedQos=void 0!==b.qos?[b.qos]:[0];b.onSuccess&&(c.onSuccess=function(a){b.onSuccess({invocationContext:b.invocationContext,grantedQos:a})});b.onFailure&&(c.onFailure=function(a){b.onFailure({invocationContext:b.invocationContext,errorCode:a,errorMessage:f(a)})});b.timeout&&(c.timeOut=new w(this,window,b.timeout,b.onFailure,[{invocationContext:b.invocationContext,errorCode:h.SUBSCRIBE_TIMEOUT.code,errorMessage:f(h.SUBSCRIBE_TIMEOUT)}]));this._requires_ack(c);
33this._schedule_message(c)};d.prototype.unsubscribe=function(a,b){this._trace("Client.unsubscribe",a,b);if(!this.connected)throw Error(f(h.INVALID_STATE,["not connected"]));var c=new q(10);c.topics=[a];b.onSuccess&&(c.callback=function(){b.onSuccess({invocationContext:b.invocationContext})});b.timeout&&(c.timeOut=new w(this,window,b.timeout,b.onFailure,[{invocationContext:b.invocationContext,errorCode:h.UNSUBSCRIBE_TIMEOUT.code,errorMessage:f(h.UNSUBSCRIBE_TIMEOUT)}]));this._requires_ack(c);this._schedule_message(c)};
34d.prototype.send=function(a){this._trace("Client.send",a);wireMessage=new q(3);wireMessage.payloadMessage=a;if(this.connected)0<a.qos?this._requires_ack(wireMessage):this.onMessageDelivered&&(this._notify_msg_sent[wireMessage]=this.onMessageDelivered(wireMessage.payloadMessage)),this._schedule_message(wireMessage);else if(this._reconnecting&&this.disconnectedPublishing){if(Object.keys(this._sentMessages).length+this._buffered_msg_queue.length>this.disconnectedBufferSize)throw Error(f(h.BUFFER_FULL,
35[this.disconnectedBufferSize]));0<a.qos?this._requires_ack(wireMessage):(wireMessage.sequence=++this._sequence,this._buffered_msg_queue.push(wireMessage))}else throw Error(f(h.INVALID_STATE,["not connected"]));};d.prototype.disconnect=function(){this._trace("Client.disconnect");this._reconnecting&&(this._reconnectTimeout.cancel(),this._reconnectTimeout=null,this._reconnecting=!1);if(!this.socket)throw Error(f(h.INVALID_STATE,["not connecting or connected"]));wireMessage=new q(14);this._notify_msg_sent[wireMessage]=
36v(this._disconnected,this);this._schedule_message(wireMessage)};d.prototype.getTraceLog=function(){if(null!==this._traceBuffer){this._trace("Client.getTraceLog",new Date);this._trace("Client.getTraceLog in flight messages",this._sentMessages.length);for(var a in this._sentMessages)this._trace("_sentMessages ",a,this._sentMessages[a]);for(a in this._receivedMessages)this._trace("_receivedMessages ",a,this._receivedMessages[a]);return this._traceBuffer}};d.prototype.startTrace=function(){null===this._traceBuffer&&
37(this._traceBuffer=[]);this._trace("Client.startTrace",new Date,"1.0.3")};d.prototype.stopTrace=function(){delete this._traceBuffer};d.prototype._doConnect=function(a){this.connectOptions.useSSL&&(a=a.split(":"),a[0]="wss",a=a.join(":"));this._wsuri=a;this.connected=!1;this.socket=4>this.connectOptions.mqttVersion?new WebSocket(a,["mqttv3.1"]):new WebSocket(a,["mqtt"]);this.socket.binaryType="arraybuffer";this.socket.onopen=v(this._on_socket_open,this);this.socket.onmessage=v(this._on_socket_message,
38this);this.socket.onerror=v(this._on_socket_error,this);this.socket.onclose=v(this._on_socket_close,this);this.sendPinger=new F(this,window,this.connectOptions.keepAliveInterval);this.receivePinger=new F(this,window,this.connectOptions.keepAliveInterval);this._connectTimeout&&(this._connectTimeout.cancel(),this._connectTimeout=null);this._connectTimeout=new w(this,window,this.connectOptions.timeout,this._disconnected,[h.CONNECT_TIMEOUT.code,f(h.CONNECT_TIMEOUT)])};d.prototype._schedule_message=function(a){this._msg_queue.push(a);
39this.connected&&this._process_queue()};d.prototype.store=function(a,b){var c={type:b.type,messageIdentifier:b.messageIdentifier,version:1};switch(b.type){case 3:b.pubRecReceived&&(c.pubRecReceived=!0);c.payloadMessage={};for(var d="",e=b.payloadMessage.payloadBytes,g=0;g<e.length;g++)d=15>=e[g]?d+"0"+e[g].toString(16):d+e[g].toString(16);c.payloadMessage.payloadHex=d;c.payloadMessage.qos=b.payloadMessage.qos;c.payloadMessage.destinationName=b.payloadMessage.destinationName;b.payloadMessage.duplicate&&
40(c.payloadMessage.duplicate=!0);b.payloadMessage.retained&&(c.payloadMessage.retained=!0);0===a.indexOf("Sent:")&&(void 0===b.sequence&&(b.sequence=++this._sequence),c.sequence=b.sequence);break;default:throw Error(f(h.INVALID_STORED_DATA,[key,c]));}localStorage.setItem(a+this._localKey+b.messageIdentifier,JSON.stringify(c))};d.prototype.restore=function(a){var b=localStorage.getItem(a),c=JSON.parse(b),d=new q(c.type,c);switch(c.type){case 3:for(var b=c.payloadMessage.payloadHex,e=new ArrayBuffer(b.length/
412),e=new Uint8Array(e),g=0;2<=b.length;){var m=parseInt(b.substring(0,2),16),b=b.substring(2,b.length);e[g++]=m}b=new Paho.MQTT.Message(e);b.qos=c.payloadMessage.qos;b.destinationName=c.payloadMessage.destinationName;c.payloadMessage.duplicate&&(b.duplicate=!0);c.payloadMessage.retained&&(b.retained=!0);d.payloadMessage=b;break;default:throw Error(f(h.INVALID_STORED_DATA,[a,b]));}0===a.indexOf("Sent:"+this._localKey)?(d.payloadMessage.duplicate=!0,this._sentMessages[d.messageIdentifier]=d):0===a.indexOf("Received:"+
42this._localKey)&&(this._receivedMessages[d.messageIdentifier]=d)};d.prototype._process_queue=function(){for(var a=null,b=this._msg_queue.reverse();a=b.pop();)this._socket_send(a),this._notify_msg_sent[a]&&(this._notify_msg_sent[a](),delete this._notify_msg_sent[a])};d.prototype._requires_ack=function(a){var b=Object.keys(this._sentMessages).length;if(b>this.maxMessageIdentifier)throw Error("Too many messages:"+b);for(;void 0!==this._sentMessages[this._message_identifier];)this._message_identifier++;
43a.messageIdentifier=this._message_identifier;this._sentMessages[a.messageIdentifier]=a;3===a.type&&this.store("Sent:",a);this._message_identifier===this.maxMessageIdentifier&&(this._message_identifier=1)};d.prototype._on_socket_open=function(){var a=new q(1,this.connectOptions);a.clientId=this.clientId;this._socket_send(a)};d.prototype._on_socket_message=function(a){this._trace("Client._on_socket_message",a.data);a=this._deframeMessages(a.data);for(var b=0;b<a.length;b+=1)this._handleMessage(a[b])};
44d.prototype._deframeMessages=function(a){a=new Uint8Array(a);var b=[];if(this.receiveBuffer){var c=new Uint8Array(this.receiveBuffer.length+a.length);c.set(this.receiveBuffer);c.set(a,this.receiveBuffer.length);a=c;delete this.receiveBuffer}try{for(c=0;c<a.length;){var d;a:{var e=a,g=c,m=g,n=e[g],l=n>>4,t=n&15,g=g+1,x=void 0,C=0,p=1;do{if(g==e.length){d=[null,m];break a}x=e[g++];C+=(x&127)*p;p*=128}while(0!==(x&128));x=g+C;if(x>e.length)d=[null,m];else{var y=new q(l);switch(l){case 2:e[g++]&1&&(y.sessionPresent=
45!0);y.returnCode=e[g++];break;case 3:var m=t>>1&3,s=256*e[g]+e[g+1],g=g+2,u=E(e,g,s),g=g+s;0<m&&(y.messageIdentifier=256*e[g]+e[g+1],g+=2);var r=new Paho.MQTT.Message(e.subarray(g,x));1==(t&1)&&(r.retained=!0);8==(t&8)&&(r.duplicate=!0);r.qos=m;r.destinationName=u;y.payloadMessage=r;break;case 4:case 5:case 6:case 7:case 11:y.messageIdentifier=256*e[g]+e[g+1];break;case 9:y.messageIdentifier=256*e[g]+e[g+1],g+=2,y.returnCode=e.subarray(g,x)}d=[y,x]}}var v=d[0],c=d[1];if(null!==v)b.push(v);else break}c<
46a.length&&(this.receiveBuffer=a.subarray(c))}catch(w){d="undefined"==w.hasOwnProperty("stack")?w.stack.toString():"No Error Stack Available";this._disconnected(h.INTERNAL_ERROR.code,f(h.INTERNAL_ERROR,[w.message,d]));return}return b};d.prototype._handleMessage=function(a){this._trace("Client._handleMessage",a);try{switch(a.type){case 2:this._connectTimeout.cancel();this._reconnectTimeout&&this._reconnectTimeout.cancel();if(this.connectOptions.cleanSession){for(var b in this._sentMessages){var c=this._sentMessages[b];
47localStorage.removeItem("Sent:"+this._localKey+c.messageIdentifier)}this._sentMessages={};for(b in this._receivedMessages){var d=this._receivedMessages[b];localStorage.removeItem("Received:"+this._localKey+d.messageIdentifier)}this._receivedMessages={}}if(0===a.returnCode)this.connected=!0,this.connectOptions.uris&&(this.hostIndex=this.connectOptions.uris.length);else{this._disconnected(h.CONNACK_RETURNCODE.code,f(h.CONNACK_RETURNCODE,[a.returnCode,H[a.returnCode]]));break}a=[];for(var e in this._sentMessages)this._sentMessages.hasOwnProperty(e)&&
48a.push(this._sentMessages[e]);if(0<this._buffered_msg_queue.length){e=null;for(var g=this._buffered_msg_queue.reverse();e=g.pop();)a.push(e),this.onMessageDelivered&&(this._notify_msg_sent[e]=this.onMessageDelivered(e.payloadMessage))}a=a.sort(function(a,b){return a.sequence-b.sequence});for(var g=0,m=a.length;g<m;g++)if(c=a[g],3==c.type&&c.pubRecReceived){var n=new q(6,{messageIdentifier:c.messageIdentifier});this._schedule_message(n)}else this._schedule_message(c);if(this.connectOptions.onSuccess)this.connectOptions.onSuccess({invocationContext:this.connectOptions.invocationContext});
49c=!1;this._reconnecting&&(c=!0,this._reconnectInterval=1,this._reconnecting=!1);this._connected(c,this._wsuri);this._process_queue();break;case 3:this._receivePublish(a);break;case 4:if(c=this._sentMessages[a.messageIdentifier])if(delete this._sentMessages[a.messageIdentifier],localStorage.removeItem("Sent:"+this._localKey+a.messageIdentifier),this.onMessageDelivered)this.onMessageDelivered(c.payloadMessage);break;case 5:if(c=this._sentMessages[a.messageIdentifier])c.pubRecReceived=!0,n=new q(6,{messageIdentifier:a.messageIdentifier}),
50this.store("Sent:",c),this._schedule_message(n);break;case 6:d=this._receivedMessages[a.messageIdentifier];localStorage.removeItem("Received:"+this._localKey+a.messageIdentifier);d&&(this._receiveMessage(d),delete this._receivedMessages[a.messageIdentifier]);var l=new q(7,{messageIdentifier:a.messageIdentifier});this._schedule_message(l);break;case 7:c=this._sentMessages[a.messageIdentifier];delete this._sentMessages[a.messageIdentifier];localStorage.removeItem("Sent:"+this._localKey+a.messageIdentifier);
51if(this.onMessageDelivered)this.onMessageDelivered(c.payloadMessage);break;case 9:if(c=this._sentMessages[a.messageIdentifier]){c.timeOut&&c.timeOut.cancel();if(128===a.returnCode[0]){if(c.onFailure)c.onFailure(a.returnCode)}else if(c.onSuccess)c.onSuccess(a.returnCode);delete this._sentMessages[a.messageIdentifier]}break;case 11:if(c=this._sentMessages[a.messageIdentifier])c.timeOut&&c.timeOut.cancel(),c.callback&&c.callback(),delete this._sentMessages[a.messageIdentifier];break;case 13:this.sendPinger.reset();
52break;case 14:this._disconnected(h.INVALID_MQTT_MESSAGE_TYPE.code,f(h.INVALID_MQTT_MESSAGE_TYPE,[a.type]));break;default:this._disconnected(h.INVALID_MQTT_MESSAGE_TYPE.code,f(h.INVALID_MQTT_MESSAGE_TYPE,[a.type]))}}catch(t){c="undefined"==t.hasOwnProperty("stack")?t.stack.toString():"No Error Stack Available",this._disconnected(h.INTERNAL_ERROR.code,f(h.INTERNAL_ERROR,[t.message,c]))}};d.prototype._on_socket_error=function(a){this._reconnecting||this._disconnected(h.SOCKET_ERROR.code,f(h.SOCKET_ERROR,
53[a.data]))};d.prototype._on_socket_close=function(){this._reconnecting||this._disconnected(h.SOCKET_CLOSE.code,f(h.SOCKET_CLOSE))};d.prototype._socket_send=function(a){if(1==a.type){var b=this._traceMask(a,"password");this._trace("Client._socket_send",b)}else this._trace("Client._socket_send",a);this.socket.send(a.encode());this.sendPinger.reset()};d.prototype._receivePublish=function(a){switch(a.payloadMessage.qos){case "undefined":case 0:this._receiveMessage(a);break;case 1:var b=new q(4,{messageIdentifier:a.messageIdentifier});
54this._schedule_message(b);this._receiveMessage(a);break;case 2:this._receivedMessages[a.messageIdentifier]=a;this.store("Received:",a);a=new q(5,{messageIdentifier:a.messageIdentifier});this._schedule_message(a);break;default:throw Error("Invaild qos\x3d"+wireMmessage.payloadMessage.qos);}};d.prototype._receiveMessage=function(a){if(this.onMessageArrived)this.onMessageArrived(a.payloadMessage)};d.prototype._connected=function(a,b){if(this.onConnected)this.onConnected(a,b)};d.prototype._reconnect=
55function(){this._trace("Client._reconnect");this.connected||(this._reconnecting=!0,this.sendPinger.cancel(),this.receivePinger.cancel(),128>this._reconnectInterval&&(this._reconnectInterval*=2),this.connectOptions.uris?(this.hostIndex=0,this._doConnect(this.connectOptions.uris[0])):this._doConnect(this.uri))};d.prototype._disconnected=function(a,b){this._trace("Client._disconnected",a,b);if(void 0!==a&&this._reconnecting)this._reconnectTimeout=new w(this,window,this._reconnectInterval,this._reconnect);
56else if(this.sendPinger.cancel(),this.receivePinger.cancel(),this._connectTimeout&&(this._connectTimeout.cancel(),this._connectTimeout=null),this._msg_queue=[],this._buffered_msg_queue=[],this._notify_msg_sent={},this.socket&&(this.socket.onopen=null,this.socket.onmessage=null,this.socket.onerror=null,this.socket.onclose=null,1===this.socket.readyState&&this.socket.close(),delete this.socket),this.connectOptions.uris&&this.hostIndex<this.connectOptions.uris.length-1)this.hostIndex++,this._doConnect(this.connectOptions.uris[this.hostIndex]);
57else if(void 0===a&&(a=h.OK.code,b=f(h.OK)),this.connected){this.connected=!1;if(this.onConnectionLost)this.onConnectionLost({errorCode:a,errorMessage:b,reconnect:this.connectOptions.reconnect,uri:this._wsuri});a!==h.OK.code&&this.connectOptions.reconnect&&(this._reconnectInterval=1,this._reconnect())}else if(4===this.connectOptions.mqttVersion&&!1===this.connectOptions.mqttVersionExplicit)this._trace("Failed to connect V4, dropping back to V3"),this.connectOptions.mqttVersion=3,this.connectOptions.uris?
58(this.hostIndex=0,this._doConnect(this.connectOptions.uris[0])):this._doConnect(this.uri);else if(this.connectOptions.onFailure)this.connectOptions.onFailure({invocationContext:this.connectOptions.invocationContext,errorCode:a,errorMessage:b})};d.prototype._trace=function(){if(this.traceFunction){for(var a in arguments)"undefined"!==typeof arguments[a]&&arguments.splice(a,1,JSON.stringify(arguments[a]));a=Array.prototype.slice.call(arguments).join("");this.traceFunction({severity:"Debug",message:a})}if(null!==
59this._traceBuffer){a=0;for(var b=arguments.length;a<b;a++)this._traceBuffer.length==this._MAX_TRACE_ENTRIES&&this._traceBuffer.shift(),0===a?this._traceBuffer.push(arguments[a]):"undefined"===typeof arguments[a]?this._traceBuffer.push(arguments[a]):this._traceBuffer.push(" "+JSON.stringify(arguments[a]))}};d.prototype._traceMask=function(a,b){var c={},d;for(d in a)a.hasOwnProperty(d)&&(c[d]=d==b?"******":a[d]);return c};var G=function(a,b,c,k){var e;if("string"!==typeof a)throw Error(f(h.INVALID_TYPE,
60[typeof a,"host"]));if(2==arguments.length){k=b;e=a;var g=e.match(/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/);if(g)a=g[4]||g[2],b=parseInt(g[7]),c=g[8];else throw Error(f(h.INVALID_ARGUMENT,[a,"host"]));}else{3==arguments.length&&(k=c,c="/mqtt");if("number"!==typeof b||0>b)throw Error(f(h.INVALID_TYPE,[typeof b,"port"]));if("string"!==typeof c)throw Error(f(h.INVALID_TYPE,[typeof c,"path"]));e="ws://"+(-1!==a.indexOf(":")&&"["!==a.slice(0,1)&&"]"!==a.slice(-1)?"["+a+"]":a)+":"+b+c}for(var m=
61g=0;m<k.length;m++){var n=k.charCodeAt(m);55296<=n&&56319>=n&&m++;g++}if("string"!==typeof k||65535<g)throw Error(f(h.INVALID_ARGUMENT,[k,"clientId"]));var l=new d(e,a,b,c,k);this._getHost=function(){return a};this._setHost=function(){throw Error(f(h.UNSUPPORTED_OPERATION));};this._getPort=function(){return b};this._setPort=function(){throw Error(f(h.UNSUPPORTED_OPERATION));};this._getPath=function(){return c};this._setPath=function(){throw Error(f(h.UNSUPPORTED_OPERATION));};this._getURI=function(){return e};
62this._setURI=function(){throw Error(f(h.UNSUPPORTED_OPERATION));};this._getClientId=function(){return l.clientId};this._setClientId=function(){throw Error(f(h.UNSUPPORTED_OPERATION));};this._getOnConnected=function(){return l.onConnected};this._setOnConnected=function(a){if("function"===typeof a)l.onConnected=a;else throw Error(f(h.INVALID_TYPE,[typeof a,"onConnected"]));};this._getDisconnectedPublishing=function(){return l.disconnectedPublishing};this._setDisconnectedPublishing=function(a){l.disconnectedPublishing=
63a};this._getDisconnectedBufferSize=function(){return l.disconnectedBufferSize};this._setDisconnectedBufferSize=function(a){l.disconnectedBufferSize=a};this._getOnConnectionLost=function(){return l.onConnectionLost};this._setOnConnectionLost=function(a){if("function"===typeof a)l.onConnectionLost=a;else throw Error(f(h.INVALID_TYPE,[typeof a,"onConnectionLost"]));};this._getOnMessageDelivered=function(){return l.onMessageDelivered};this._setOnMessageDelivered=function(a){if("function"===typeof a)l.onMessageDelivered=
64a;else throw Error(f(h.INVALID_TYPE,[typeof a,"onMessageDelivered"]));};this._getOnMessageArrived=function(){return l.onMessageArrived};this._setOnMessageArrived=function(a){if("function"===typeof a)l.onMessageArrived=a;else throw Error(f(h.INVALID_TYPE,[typeof a,"onMessageArrived"]));};this._getTrace=function(){return l.traceFunction};this._setTrace=function(a){if("function"===typeof a)l.traceFunction=a;else throw Error(f(h.INVALID_TYPE,[typeof a,"onTrace"]));};this.connect=function(a){a=a||{};z(a,
65{timeout:"number",userName:"string",password:"string",willMessage:"object",keepAliveInterval:"number",cleanSession:"boolean",useSSL:"boolean",invocationContext:"object",onSuccess:"function",onFailure:"function",hosts:"object",ports:"object",reconnect:"boolean",mqttVersion:"number",mqttVersionExplicit:"boolean",uris:"object"});void 0===a.keepAliveInterval&&(a.keepAliveInterval=60);if(4<a.mqttVersion||3>a.mqttVersion)throw Error(f(h.INVALID_ARGUMENT,[a.mqttVersion,"connectOptions.mqttVersion"]));void 0===
66a.mqttVersion?(a.mqttVersionExplicit=!1,a.mqttVersion=4):a.mqttVersionExplicit=!0;if(void 0!==a.password&&void 0===a.userName)throw Error(f(h.INVALID_ARGUMENT,[a.password,"connectOptions.password"]));if(a.willMessage){if(!(a.willMessage instanceof r))throw Error(f(h.INVALID_TYPE,[a.willMessage,"connectOptions.willMessage"]));a.willMessage.stringPayload=null;if("undefined"===typeof a.willMessage.destinationName)throw Error(f(h.INVALID_TYPE,[typeof a.willMessage.destinationName,"connectOptions.willMessage.destinationName"]));
67}"undefined"===typeof a.cleanSession&&(a.cleanSession=!0);if(a.hosts){if(!(a.hosts instanceof Array))throw Error(f(h.INVALID_ARGUMENT,[a.hosts,"connectOptions.hosts"]));if(1>a.hosts.length)throw Error(f(h.INVALID_ARGUMENT,[a.hosts,"connectOptions.hosts"]));for(var b=!1,d=0;d<a.hosts.length;d++){if("string"!==typeof a.hosts[d])throw Error(f(h.INVALID_TYPE,[typeof a.hosts[d],"connectOptions.hosts["+d+"]"]));if(/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/.test(a.hosts[d]))if(0===d)b=!0;else{if(!b)throw Error(f(h.INVALID_ARGUMENT,
68[a.hosts[d],"connectOptions.hosts["+d+"]"]));}else if(b)throw Error(f(h.INVALID_ARGUMENT,[a.hosts[d],"connectOptions.hosts["+d+"]"]));}if(b)a.uris=a.hosts;else{if(!a.ports)throw Error(f(h.INVALID_ARGUMENT,[a.ports,"connectOptions.ports"]));if(!(a.ports instanceof Array))throw Error(f(h.INVALID_ARGUMENT,[a.ports,"connectOptions.ports"]));if(a.hosts.length!==a.ports.length)throw Error(f(h.INVALID_ARGUMENT,[a.ports,"connectOptions.ports"]));a.uris=[];for(d=0;d<a.hosts.length;d++){if("number"!==typeof a.ports[d]||
690>a.ports[d])throw Error(f(h.INVALID_TYPE,[typeof a.ports[d],"connectOptions.ports["+d+"]"]));var b=a.hosts[d],g=a.ports[d];e="ws://"+(-1!==b.indexOf(":")?"["+b+"]":b)+":"+g+c;a.uris.push(e)}}}l.connect(a)};this.subscribe=function(a,b){if("string"!==typeof a)throw Error("Invalid argument:"+a);b=b||{};z(b,{qos:"number",invocationContext:"object",onSuccess:"function",onFailure:"function",timeout:"number"});if(b.timeout&&!b.onFailure)throw Error("subscribeOptions.timeout specified with no onFailure callback.");
70if("undefined"!==typeof b.qos&&0!==b.qos&&1!==b.qos&&2!==b.qos)throw Error(f(h.INVALID_ARGUMENT,[b.qos,"subscribeOptions.qos"]));l.subscribe(a,b)};this.unsubscribe=function(a,b){if("string"!==typeof a)throw Error("Invalid argument:"+a);b=b||{};z(b,{invocationContext:"object",onSuccess:"function",onFailure:"function",timeout:"number"});if(b.timeout&&!b.onFailure)throw Error("unsubscribeOptions.timeout specified with no onFailure callback.");l.unsubscribe(a,b)};this.send=function(a,b,c,d){var e;if(0===
71arguments.length)throw Error("Invalid argument.length");if(1==arguments.length){if(!(a instanceof r)&&"string"!==typeof a)throw Error("Invalid argument:"+typeof a);e=a;if("undefined"===typeof e.destinationName)throw Error(f(h.INVALID_ARGUMENT,[e.destinationName,"Message.destinationName"]));}else e=new r(b),e.destinationName=a,3<=arguments.length&&(e.qos=c),4<=arguments.length&&(e.retained=d);l.send(e)};this.publish=function(a,b,c,d){console.log("Publising message to: ",a);var e;if(0===arguments.length)throw Error("Invalid argument.length");
72if(1==arguments.length){if(!(a instanceof r)&&"string"!==typeof a)throw Error("Invalid argument:"+typeof a);e=a;if("undefined"===typeof e.destinationName)throw Error(f(h.INVALID_ARGUMENT,[e.destinationName,"Message.destinationName"]));}else e=new r(b),e.destinationName=a,3<=arguments.length&&(e.qos=c),4<=arguments.length&&(e.retained=d);l.send(e)};this.disconnect=function(){l.disconnect()};this.getTraceLog=function(){return l.getTraceLog()};this.startTrace=function(){l.startTrace()};this.stopTrace=
73function(){l.stopTrace()};this.isConnected=function(){return l.connected}};G.prototype={get host(){return this._getHost()},set host(a){this._setHost(a)},get port(){return this._getPort()},set port(a){this._setPort(a)},get path(){return this._getPath()},set path(a){this._setPath(a)},get clientId(){return this._getClientId()},set clientId(a){this._setClientId(a)},get onConnected(){return this._getOnConnected()},set onConnected(a){this._setOnConnected(a)},get disconnectedPublishing(){return this._getDisconnectedPublishing()},
74set disconnectedPublishing(a){this._setDisconnectedPublishing(a)},get disconnectedBufferSize(){return this._getDisconnectedBufferSize()},set disconnectedBufferSize(a){this._setDisconnectedBufferSize(a)},get onConnectionLost(){return this._getOnConnectionLost()},set onConnectionLost(a){this._setOnConnectionLost(a)},get onMessageDelivered(){return this._getOnMessageDelivered()},set onMessageDelivered(a){this._setOnMessageDelivered(a)},get onMessageArrived(){return this._getOnMessageArrived()},set onMessageArrived(a){this._setOnMessageArrived(a)},
75get trace(){return this._getTrace()},set trace(a){this._setTrace(a)}};var r=function(a){var b;if("string"===typeof a||a instanceof ArrayBuffer||a instanceof Int8Array||a instanceof Uint8Array||a instanceof Int16Array||a instanceof Uint16Array||a instanceof Int32Array||a instanceof Uint32Array||a instanceof Float32Array||a instanceof Float64Array)b=a;else throw f(h.INVALID_ARGUMENT,[a,"newPayload"]);this._getPayloadString=function(){return"string"===typeof b?b:E(b,0,b.length)};this._getPayloadBytes=
76function(){if("string"===typeof b){var a=new ArrayBuffer(n(b)),a=new Uint8Array(a);D(b,a,0);return a}return b};var c;this._getDestinationName=function(){return c};this._setDestinationName=function(a){if("string"===typeof a)c=a;else throw Error(f(h.INVALID_ARGUMENT,[a,"newDestinationName"]));};var d=0;this._getQos=function(){return d};this._setQos=function(a){if(0===a||1===a||2===a)d=a;else throw Error("Invalid argument:"+a);};var e=!1;this._getRetained=function(){return e};this._setRetained=function(a){if("boolean"===
77typeof a)e=a;else throw Error(f(h.INVALID_ARGUMENT,[a,"newRetained"]));};var g=!1;this._getDuplicate=function(){return g};this._setDuplicate=function(a){g=a}};r.prototype={get payloadString(){return this._getPayloadString()},get payloadBytes(){return this._getPayloadBytes()},get destinationName(){return this._getDestinationName()},set destinationName(a){this._setDestinationName(a)},get topic(){return this._getDestinationName()},set topic(a){this._setDestinationName(a)},get qos(){return this._getQos()},
78set qos(a){this._setQos(a)},get retained(){return this._getRetained()},set retained(a){this._setRetained(a)},get duplicate(){return this._getDuplicate()},set duplicate(a){this._setDuplicate(a)}};return{Client:G,Message:r}}(window)});

F. paho.javascript-1.0.3 / paho-mqtt.js

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

I. Resumen