├── .gitignore
├── Gruntfile.js
├── README.md
├── WebSocket.js
├── WebSocketManager.js
├── bower.json
├── demo
├── demo.js
├── index.html
└── server.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | **/*.*~
2 | node_modules/
3 | bower_components/
4 | .idea/
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 | require('load-grunt-tasks')(grunt);
3 |
4 | grunt.initConfig ({
5 | uglify: {
6 | dist: {
7 | files: {
8 | 'WebSocket.min.js': 'WebSocket.js' ,
9 | 'WebSocketManager.min.js': 'WebSocketManager.js'
10 | }
11 | }
12 | } ,
13 | jshint: {
14 | dist: {
15 | options: {
16 | globals: {
17 | Ext: true
18 | } ,
19 | eqeqeq: true ,
20 | undef: true ,
21 | eqnull: true ,
22 | browser: true ,
23 | smarttabs: true ,
24 | loopfunc: true
25 | } ,
26 | src: ['WebSocket.js', 'WebSocketManager.js']
27 | }
28 | }
29 | });
30 |
31 | grunt.registerTask ('check', ['jshint']);
32 | grunt.registerTask ('minify', ['uglify']);
33 | grunt.registerTask ('build', ['check', 'minify']);
34 | };
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ExtJS-WebSocket
2 |
3 | # This library is no longer mainteined.
4 |
5 | ExtJS-WebSocket is an extension to handle and use the HTML5 WebSocket with ExtJS.
6 |
7 | It has two classes: `Ext.ux.WebSocket` and `Ext.ux.WebSocketManager`
8 | The first one is a wrapper for standard HTML5 WebSocket and it provides a lot of interesting and easy-to-use features.
9 | The second one is a singleton to register different Ext.ux.WebSocket and it provides functions to work with every registered websocket at the same time.
10 |
11 | ## ExtJS 5
12 | The new version of ExtJS 5 has requested to make a new major version of `ExtJS-WebSocket`.
13 | Now, this new major version **v1.0.0** is located on the master branch.
14 |
15 | ## ExtJS 4 & Sencha Touch 2
16 | It's possible to work either with ExtJS 4 and Sencha Touch 2 with previous version **v0.0.5**
17 |
18 | ## Install via Bower
19 | First of all, install [**Bower**](http://bower.io/).
20 |
21 | Then install `Ext.ux.WebSocket` (version v1.x.x for ExtJS 5):
22 |
23 | ```bash
24 | $ bower install ext.ux.websocket
25 | ```
26 |
27 | Or install `Ext.ux.WebSocket` (version v0.0.5 for ExtJS 4 & Sencha Touch 2):
28 |
29 | ```bash
30 | $ bower install ext.ux.websocket#0.0.5
31 | ```
32 |
33 | Now, you got the extension at the following path: *YOUR_PROJECT_PATH/bower_components/ext.ux.websocket/*
34 |
35 | It contains **WebSocket.js** and **WebSocketManager.js** files.
36 |
37 | Let's setup the **Ext.Loader** to require the right file:
38 |
39 | ```javascript
40 | Ext.Loader.setConfig ({
41 | enabled: true ,
42 | paths: {
43 | 'Ext.ux.WebSocket': 'bower_components/ext.ux.websocket/WebSocket.js' ,
44 | 'Ext.ux.WebSocketManager': 'bower_components/ext.ux.websocket/WebSocketManager.js'
45 | }
46 | });
47 |
48 | Ext.require (['Ext.ux.WebSocket', 'Ext.ux.WebSocketManager']);
49 | ```
50 |
51 | ## Usage
52 | Load `Ext.ux.WebSocket` and `Ext.ux.WebSocketManager` via `Ext.require`:
53 |
54 | ```javascript
55 | Ext.Loader.setConfig ({
56 | enabled: true
57 | });
58 |
59 | Ext.require (['Ext.ux.WebSocket', 'Ext.ux.WebSocketManager']);
60 | ```
61 |
62 | Now, you are ready to use them in your code as follows:
63 |
64 | ```javascript
65 | // Creating a new instance of Ext.ux.WebSocket
66 | var ws = Ext.create ('Ext.ux.WebSocket', {
67 | url: 'your_url:your_port' ,
68 | protocol: 'your_protocol'
69 | });
70 |
71 | // Using Ext.ux.WebSocketManager
72 | Ext.ux.WebSocketManager.register (ws);
73 | ```
74 |
75 | ## Communications supported
76 | ### Pure text communication
77 | The communication is text-only, without objects or any other kind of data.
78 |
79 | ```javascript
80 | var websocket = Ext.create ('Ext.ux.WebSocket', {
81 | url: 'ws://localhost:8888' ,
82 | listeners: {
83 | open: function (ws) {
84 | console.log ('The websocket is ready to use');
85 | ws.send ('This is a simple text');
86 | } ,
87 | close: function (ws) {
88 | console.log ('The websocket is closed!');
89 | } ,
90 | error: function (ws, error) {
91 | Ext.Error.raise (error);
92 | } ,
93 | message: function (ws, message) {
94 | console.log ('A new message is arrived: ' + message);
95 | }
96 | }
97 | });
98 | ```
99 |
100 | ### Pure event-driven communication
101 | The communication is event-driven: an event and a String or Object are sent and the websocket handles different events.
102 |
103 | ```javascript
104 | var websocket = Ext.create ('Ext.ux.WebSocket', {
105 | url: 'ws://localhost:8888' ,
106 | listeners: {
107 | open: function (ws) {
108 | console.log ('The websocket is ready to use');
109 | ws.send ('init', 'This is a simple text');
110 | ws.send ('and continue', {
111 | 'my': 'data' ,
112 | 'your': 'data'
113 | });
114 | } ,
115 | close: function (ws) {
116 | console.log ('The websocket is closed!');
117 | }
118 | }
119 | });
120 |
121 | // A 'stop' event is sent from the server
122 | // 'data' has 'cmd' and 'msg' fields
123 | websocket.on ('stop', function (data) {
124 | console.log ('Command: ' + data.cmd);
125 | console.log ('Message: ' + data.msg);
126 | });
127 | ```
128 |
129 | ### Mixed communication
130 | The communication is mixed: it can handles text-only and event-driven communication.
131 |
132 | ```javascript
133 | var websocket = Ext.create ('Ext.ux.WebSocket', {
134 | url: 'ws://localhost:8888' ,
135 | listeners: {
136 | open: function (ws) {
137 | console.log ('The websocket is ready to use');
138 | ws.send ('This is only-text message');
139 | ws.send ('init', 'This is a simple text');
140 | ws.send ('and continue', {
141 | 'my': 'data' ,
142 | 'your': 'data'
143 | });
144 | } ,
145 | close: function (ws) {
146 | console.log ('The websocket is closed!');
147 | } ,
148 | message: function (ws, message) {
149 | console.log ('Text-only message arrived is: ' + message);
150 | }
151 | }
152 | });
153 |
154 | // A 'stop' event is sent from the server
155 | // 'data' has 'cmd' and 'msg' fields
156 | websocket.on ('stop', function (data) {
157 | console.log ('Command: ' + data.cmd);
158 | console.log ('Message: ' + data.msg);
159 | });
160 | ```
161 |
162 | ## Ext.ux.WebSocketManager features
163 | Here's an example of the manager:
164 |
165 | ```javascript
166 | var ws1 = Ext.create ('Ext.ux.WebSocket', {
167 | url: 'ws://localhost:8888'
168 | });
169 |
170 | Ext.ux.WebSocketManager.register (ws1);
171 |
172 | var ws2 = Ext.create ('Ext.ux.WebSocket', {
173 | url: 'ws://localhost:8900'
174 | });
175 |
176 | Ext.ux.WebSocketManager.register (ws2);
177 |
178 | var ws3 = Ext.create ('Ext.ux.WebSocket', {
179 | url: 'ws://localhost:8950'
180 | });
181 |
182 | Ext.ux.WebSocketManager.register (ws3);
183 |
184 | Ext.ux.WebSocketManager.listen ('system shutdown', function (ws, data) {
185 | Ext.Msg.show ({
186 | title: 'System Shutdown' ,
187 | msg: data ,
188 | icon: Ext.Msg.WARNING ,
189 | buttons: Ext.Msg.OK
190 | });
191 | });
192 |
193 | // This will be handled by everyone
194 | Ext.ux.WebSocketManager.broadcast ('system shutdown', 'BROADCAST: the system will shutdown in few minutes.');
195 |
196 | Ext.ux.WebSocketManager.closeAll ();
197 |
198 | Ext.ux.WebSocketManager.unregister (ws1);
199 | Ext.ux.WebSocketManager.unregister (ws2);
200 | Ext.ux.WebSocketManager.unregister (ws3);
201 | ```
202 |
203 | ## Cool Configurations
204 | Follows some cool configurations to customize `Ext.ux.WebSocket`
205 |
206 | ### autoReconnect
207 | By default, `Ext.ux.WebSocket` tries to re-open the connection with an internal task if it gets closed: it tries every 5 seconds.
208 | Now, if you want to disable this behaviour, set autoReconnect to false:
209 |
210 | ```javascript
211 | var websocket = Ext.create ('Ext.ux.WebSocket', {
212 | url: 'ws://localhost:8888' ,
213 | autoReconnect: false
214 | });
215 | ```
216 |
217 | In this case, when the server listens again, the websocket won't estabilish the connection.
218 | Otherwise, if you want to change the reconnect timeslice, set autoReconnectionInterval in milliseconds:
219 |
220 | ```javascript
221 | var websocket = Ext.create ('Ext.ux.WebSocket', {
222 | url: 'ws://localhost:8888' ,
223 | autoReconnect: true ,
224 | autoReconnectInterval: 1000
225 | });
226 | ```
227 |
228 | While in this case, the websocket tries to re-open the connection with the server every second.
229 |
230 | ### lazyConnection
231 | Setting this config to true it will let the websocket to open the connection after its initialization:
232 |
233 | ```javascript
234 | var websocket = Ext.create ('Ext.ux.WebSocket', {
235 | url: 'ws://localhost:8888' ,
236 | lazyConnection: true
237 | });
238 |
239 | // other stuff
240 |
241 | websocket.open ();
242 | ```
243 |
244 | When the **open** method is called, the websocket open the connection with the server.
245 | By default, this config is set to **false**
246 |
247 | ### keepUnsentMessages
248 | The connection gets closed by the server but the websocket still sends messages to it.
249 | By default, those messages are discarded but if you want to keep them and then send them back after the connection is re-estabilished, just set keepUnsentMessages to true:
250 |
251 | ```javascript
252 | var websocket = Ext.create ('Ext.ux.WebSocket', {
253 | url: 'ws://localhost:8888' ,
254 | keepUnsentMessages: true
255 | });
256 | ```
257 |
258 | The websocket will send every unsent messages in one shot.
259 |
260 | ## Run the demo
261 | First of all, you need [**NodeJS**](http://nodejs.org/) and [**NPM**](https://www.npmjs.org/).
262 |
263 | Then, install every dependencies:
264 |
265 | ```bash
266 | $ npm install
267 | ```
268 |
269 | Open a websocket for each port you want:
270 |
271 | ```bash
272 | $ node demo/server.js 9001 9002 9003
273 | ```
274 |
275 | Now, you have three websockets listening at 9001, 9002 and 9003 port on the server side!
276 | Then, type in the address bar of your browser: **http://localhost/ExtJS-WebSocket/demo** and play the demo ;)
277 |
278 | ## Documentation
279 | You can build the documentation (like ExtJS Docs) with [**jsduck**](https://github.com/senchalabs/jsduck):
280 |
281 | ```bash
282 | $ jsduck ux --output /var/www/docs
283 | ```
284 |
285 | It will make the documentation into docs dir and it will be visible at: http://localhost/docs
286 |
287 | ## License
288 | The MIT License (MIT)
289 |
290 | Copyright (c) 2013 Vincenzo Ferrari
291 |
292 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
293 |
294 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
295 |
296 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
297 |
--------------------------------------------------------------------------------
/WebSocket.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Ext.ux.WebSocket
3 | * @author Vincenzo Ferrari
4 | *
5 | * Wrapper for HTML5 WebSocket
6 | *
7 | * This class provide an interface for HTML5 WebSocket.
8 | *
9 | *
Pure text communication
10 | * The communication is text-only, without objects or any other kind of data.
11 | *
12 | * var websocket = Ext.create ('Ext.ux.WebSocket', {
13 | * url: 'ws://localhost:8888' ,
14 | * listeners: {
15 | * open: function (ws) {
16 | * console.log ('The websocket is ready to use');
17 | * ws.send ('This is a simple text');
18 | * } ,
19 | * close: function (ws) {
20 | * console.log ('The websocket is closed!');
21 | * } ,
22 | * error: function (ws, error) {
23 | * Ext.Error.raise (error);
24 | * } ,
25 | * message: function (ws, message) {
26 | * console.log ('A new message is arrived: ' + message);
27 | * }
28 | * }
29 | * });
30 | *
31 | *
Pure event-driven communication
32 | * The communication is event-driven: an event and a String or Object are sent and the websocket handles different events.
33 | *
34 | * var websocket = Ext.create ('Ext.ux.WebSocket', {
35 | * url: 'ws://localhost:8888' ,
36 | * listeners: {
37 | * open: function (ws) {
38 | * console.log ('The websocket is ready to use');
39 | * ws.send ('init', 'This is a simple text');
40 | * ws.send ('and continue', {
41 | * 'my': 'data' ,
42 | * 'your': 'data'
43 | * });
44 | * } ,
45 | * close: function (ws) {
46 | * console.log ('The websocket is closed!');
47 | * }
48 | * }
49 | * });
50 | *
51 | * // A 'stop' event is sent from the server
52 | * // 'data' has 'cmd' and 'msg' fields
53 | * websocket.on ('stop', function (data) {
54 | * console.log ('Command: ' + data.cmd);
55 | * console.log ('Message: ' + data.msg);
56 | * });
57 | *
58 | *
Mixed event-driven and text communication
59 | * The communication is mixed: it can handles text-only and event-driven communication.
60 | *
61 | * var websocket = Ext.create ('Ext.ux.WebSocket', {
62 | * url: 'ws://localhost:8888' ,
63 | * listeners: {
64 | * open: function (ws) {
65 | * console.log ('The websocket is ready to use');
66 | * ws.send ('This is only-text message');
67 | * ws.send ('init', 'This is a simple text');
68 | * ws.send ('and continue', {
69 | * 'my': 'data' ,
70 | * 'your': 'data'
71 | * });
72 | * } ,
73 | * close: function (ws) {
74 | * console.log ('The websocket is closed!');
75 | * } ,
76 | * message: function (ws, message) {
77 | * console.log ('Text-only message arrived is: ' + message);
78 | * }
79 | * }
80 | * });
81 | *
82 | * // A 'stop' event is sent from the server
83 | * // 'data' has 'cmd' and 'msg' fields
84 | * websocket.on ('stop', function (data) {
85 | * console.log ('Command: ' + data.cmd);
86 | * console.log ('Message: ' + data.msg);
87 | * });
88 | */
89 |
90 | Ext.define('Ext.ux.WebSocket', {
91 | alias: 'websocket',
92 |
93 | mixins: {
94 | observable: 'Ext.util.Observable'
95 | },
96 |
97 | requires: ['Ext.util.TaskManager', 'Ext.util.Memento'],
98 |
99 | /**
100 | * @event open
101 | * Fires after the websocket has been connected.
102 | * @param {Ext.ux.WebSocket} this The websocket
103 | */
104 |
105 | /**
106 | * @event error
107 | * Fires after an error occured
108 | * @param {Ext.ux.WebSocket} this The websocket
109 | * @param {Object} error The error object to display
110 | */
111 |
112 | /**
113 | * @event close
114 | * Fires after the websocket has been disconnected.
115 | * @param {Ext.ux.WebSocket} this The websocket
116 | */
117 |
118 | /**
119 | * @event message
120 | * Fires after a message is arrived from the server.
121 | * @param {Ext.ux.WebSocket} this The websocket
122 | * @param {String/Object} message The message arrived
123 | */
124 |
125 | config: {
126 | /**
127 | * @cfg {String} url (required) The URL to connect
128 | */
129 | url: '',
130 |
131 | /**
132 | * @cfg {String} protocol The protocol to use in the connection
133 | */
134 | protocol: null,
135 |
136 | /**
137 | * @cfg {String} communicationType The type of communication. 'both' (default) for event-driven and pure-text communication, 'event' for only event-driven and 'text' for only pure-text.
138 | */
139 | communicationType: 'both',
140 |
141 | /**
142 | * @cfg {Boolean} autoReconnect If the connection is closed by the server, it tries to re-connect again. The execution interval time of this operation is specified in autoReconnectInterval
143 | */
144 | autoReconnect: true,
145 |
146 | /**
147 | * @cfg {Int} autoReconnectInterval Execution time slice of the autoReconnect operation, specified in milliseconds.
148 | */
149 | autoReconnectInterval: 5000,
150 |
151 | /**
152 | * @cfg {Boolean} lazyConnection Connect the websocket after the initialization with the open method
153 | */
154 | lazyConnection: false,
155 |
156 | /**
157 | * @cfg {Boolean} keepUnsentMessages Keep unsent messages and try to send them back after the connection is open again
158 | */
159 | keepUnsentMessages: false
160 | },
161 |
162 | /**
163 | * @property {Number} CONNECTING
164 | * @readonly
165 | * The connection is not yet open.
166 | */
167 | CONNECTING: 0,
168 |
169 | /**
170 | * @property {Number} OPEN
171 | * @readonly
172 | * The connection is open and ready to communicate.
173 | */
174 | OPEN: 1,
175 |
176 | /**
177 | * @property {Number} CLOSING
178 | * @readonly
179 | * The connection is in the process of closing.
180 | */
181 | CLOSING: 2,
182 |
183 | /**
184 | * @property {Number} CLOSED
185 | * @readonly
186 | * The connection is closed or couldn't be opened.
187 | */
188 | CLOSED: 3,
189 |
190 | /**
191 | * @property {Object} memento
192 | * @private
193 | * Internal memento
194 | */
195 | memento: {},
196 |
197 | /**
198 | * @property {Array} memento
199 | * @private
200 | * Internal queue of unsent messages
201 | */
202 | messageQueue: [],
203 |
204 | /**
205 | * Creates new WebSocket
206 | * @param {String/Object} config The configuration options may be specified as follows:
207 | *
208 | * // with a configuration set
209 | * var config = {
210 | * url: 'your_url' ,
211 | * protocol: 'your_protocol'
212 | * };
213 | *
214 | * var ws = Ext.create ('Ext.ux.WebSocket', config);
215 | *
216 | * // or with websocket url only
217 | * var ws = Ext.create ('Ext.ux.WebSocket', 'ws://localhost:30000');
218 | *
219 | * @return {Ext.ux.WebSocket} An instance of Ext.ux.WebSocket or null if an error occurred.
220 | */
221 | constructor: function (cfg) {
222 | var me = this;
223 |
224 | // Raises an error if no url is given
225 | if (Ext.isEmpty(cfg)) {
226 | Ext.Error.raise('URL for the websocket is required!');
227 | return null;
228 | }
229 |
230 | // Allows initialization with string
231 | // e.g.: Ext.create ('Ext.ux.WebSocket', 'ws://localhost:8888');
232 | if (typeof cfg === 'string') {
233 | cfg = {
234 | url: cfg
235 | };
236 | }
237 |
238 | me.initConfig(cfg);
239 | me.mixins.observable.constructor.call(me, cfg);
240 |
241 | try {
242 | // Initializes internal websocket
243 | if (!me.getLazyConnection()) me.initWebsocket();
244 |
245 | me.memento = Ext.create('Ext.util.Memento');
246 | me.memento.capture('autoReconnect', me);
247 | }
248 | catch (err) {
249 | Ext.Error.raise(err);
250 | return null;
251 | }
252 |
253 | return me;
254 | },
255 |
256 | /**
257 | * @method isReady
258 | * Returns if the websocket connection is up or not
259 | * @return {Boolean} True if the connection is up, False otherwise
260 | */
261 | isReady: function () {
262 | return this.getStatus() === this.OPEN;
263 | },
264 |
265 | /**
266 | * @method getStatus
267 | * Returns the current status of the websocket
268 | * @return {Number} The current status of the websocket (0: connecting, 1: open, 2: closed)
269 | */
270 | getStatus: function () {
271 | return this.ws.readyState;
272 | },
273 |
274 | /**
275 | * @method close
276 | * Closes the websocket and kills the autoreconnect task, if exists
277 | * @return {Ext.ux.WebSocket} The websocket
278 | */
279 | close: function () {
280 | var me = this;
281 |
282 | if (me.autoReconnectTask) {
283 | Ext.TaskManager.stop(me.autoReconnectTask);
284 | delete me.autoReconnectTask;
285 | }
286 | // Deactivate autoReconnect until the websocket is open again
287 | me.setAutoReconnect(false);
288 |
289 | me.ws.close();
290 |
291 | return me;
292 | },
293 |
294 | /**
295 | * @method open
296 | * Re/Open the websocket
297 | * @return {Ext.ux.WebSocket} The websocket
298 | */
299 | open: function () {
300 | var me = this;
301 |
302 | // Restore autoReconnect initial value
303 | me.memento.restore('autoReconnect', false, me);
304 | me.initWebsocket();
305 |
306 | return me;
307 | },
308 |
309 | /**
310 | * @method send
311 | * Sends a message.
312 | * This method is bind at run-time level because it changes on the websocket initial configuration.
313 | * It supports three kind of communication:
314 | *
315 | * 1. text-only
316 | * Syntax: ws.send (string);
317 | * Example: ws.send ('hello world!');
318 | * 2. event-driven
319 | * Syntax: ws.send (event, string/object);
320 | * Example 1: ws.send ('greetings', 'hello world!');
321 | * Example 2: ws.send ('greetings', {text: 'hello world!'});
322 | * 3. hybrid (text and event)
323 | * It uses both: see examples above
324 | * @param {String/Object} message Can be a single text message or an association of event/message.
325 | */
326 | send: function () {
327 | },
328 |
329 | /**
330 | * @method initWebsocket
331 | * Internal websocket initialization
332 | * @private
333 | */
334 | initWebsocket: function () {
335 | var me = this;
336 |
337 | me.ws = Ext.isEmpty(me.getProtocol()) ? new WebSocket(me.getUrl()) : new WebSocket(me.getUrl(), me.getProtocol());
338 |
339 | me.ws.onopen = function (evt) {
340 | // Kills the auto reconnect task
341 | // It will be reactivated at the next onclose event
342 | if (me.autoReconnectTask) {
343 | Ext.TaskManager.stop(me.autoReconnectTask);
344 | delete me.autoReconnectTask;
345 | }
346 |
347 | // Flush unset messages
348 | if (me.getKeepUnsentMessages() && me.messageQueue.length > 0) {
349 | while (me.messageQueue.length > 0) {
350 | // Avoid infinite loop into safeSend method
351 | if (me.isReady()) me.safeSend(me.messageQueue.shift());
352 | else break;
353 | }
354 | }
355 |
356 | me.fireEvent('open', me);
357 | };
358 |
359 | me.ws.onerror = function (error) {
360 | me.fireEvent('error', me, error);
361 | };
362 |
363 | me.ws.onclose = function (evt) {
364 | me.fireEvent('close', me);
365 |
366 | // Setups the auto reconnect task, just one
367 | if (me.getAutoReconnect() && (typeof me.autoReconnectTask === 'undefined')) {
368 | me.autoReconnectTask = Ext.TaskManager.start({
369 | run: function () {
370 | // It reconnects only if it's disconnected
371 | if (me.getStatus() === me.CLOSED) {
372 | me.initWebsocket();
373 | }
374 | },
375 | interval: me.getAutoReconnectInterval()
376 | });
377 | }
378 | };
379 |
380 | if (me.getCommunicationType() === 'both') {
381 | me.ws.onmessage = Ext.bind(me.receiveBothMessage, this);
382 | me.send = Ext.bind(me.sendBothMessage, this);
383 | }
384 | else if (me.getCommunicationType() === 'event') {
385 | me.ws.onmessage = Ext.bind(me.receiveEventMessage, this);
386 | me.send = Ext.bind(me.sendEventMessage, this);
387 | }
388 | else {
389 | me.ws.onmessage = Ext.bind(me.receiveTextMessage, this);
390 | me.send = Ext.bind(me.sendTextMessage, this);
391 | }
392 | },
393 |
394 | /**
395 | * @method flush
396 | * It sends every message given to the websocket, checking first if is there any connection
397 | * If there's no connection, it enqueues the message and flushes it later
398 | * @param {String} Data to send
399 | * @return {Ext.ux.WebSocket} The websocket
400 | * @private
401 | */
402 | safeSend: function (data) {
403 | var me = this;
404 |
405 | if (me.isReady()) me.ws.send(data);
406 | else if (me.getKeepUnsentMessages()) me.messageQueue.push(data);
407 |
408 | return me;
409 | },
410 |
411 | /**
412 | * @method receiveBothMessage
413 | * It catches every event-driven and pure text messages incoming from the server
414 | * @param {Object} message Message incoming from the server
415 | * @private
416 | */
417 | receiveBothMessage: function (message) {
418 | var me = this;
419 |
420 | try {
421 | /*
422 | message.data : JSON encoded message
423 | msg.event : event to be raise
424 | msg.data : data to be handle
425 | */
426 | var msg = Ext.JSON.decode(message.data);
427 | me.fireEvent(msg.event, me, msg.data);
428 | me.fireEvent('message', me, msg);
429 | }
430 | catch (err) {
431 | if (Ext.isString(message.data)) me.fireEvent(message.data, me, message.data);
432 | // Message event is always sent
433 | me.fireEvent('message', me, message.data);
434 | }
435 | },
436 |
437 | /**
438 | * @method receiveEventMessage
439 | * It catches every event-driven messages incoming from the server
440 | * @param {Object} message Message incoming from the server
441 | * @private
442 | */
443 | receiveEventMessage: function (message) {
444 | var me = this;
445 |
446 | try {
447 | var msg = Ext.JSON.decode(message.data);
448 | me.fireEvent(msg.event, me, msg.data);
449 | me.fireEvent('message', me, msg);
450 | }
451 | catch (err) {
452 | Ext.Error.raise(err);
453 | }
454 | },
455 |
456 | /**
457 | * @method receiveTextMessage
458 | * It catches every pure text messages incoming from the server
459 | * @param {Object} message Message incoming from the server
460 | * @private
461 | */
462 | receiveTextMessage: function (message) {
463 | var me = this;
464 |
465 | try {
466 | me.fireEvent(message, me, message);
467 | // Message event is always sent
468 | me.fireEvent('message', me, message);
469 | }
470 | catch (err) {
471 | Ext.Error.raise(err);
472 | }
473 | },
474 |
475 | /**
476 | * @method sendBothMessage
477 | * It sends both pure text and event-driven messages to the server
478 | * @param {String/String[]} events Message(s) or event(s) to send to the server
479 | * @param {String/Object} data Message to send to the server, associated to its event
480 | * @return {Ext.ux.WebSocket} The websocket
481 | * @private
482 | */
483 | sendBothMessage: function (events, data) {
484 | var me = this;
485 |
486 | // Treats it as normal message
487 | if (arguments.length === 1) {
488 | if (Ext.isString(events)) me.safeSend(events);
489 | else Ext.Error.raise('String expected!');
490 | }
491 | // Treats it as event-driven message
492 | else if (arguments.length >= 2) {
493 | events = Ext.isString(events) ? [events] : events;
494 |
495 | for (var i = 0; i < events.length; i++) {
496 | var msg = {
497 | event: events[i],
498 | data: data
499 | };
500 |
501 | me.safeSend(Ext.JSON.encode(msg));
502 | }
503 | }
504 |
505 | return me;
506 | },
507 |
508 | /**
509 | * @method sendEventMessage
510 | * It sends event-driven messages to the server
511 | * @param {String/String[]} events Event(s) to send to the server
512 | * @param {String/Object} data Message to send to the server, associated to its event(s)
513 | * @return {Ext.ux.WebSocket} The websocket
514 | * @private
515 | */
516 | sendEventMessage: function (events, data) {
517 | var me = this;
518 |
519 | events = Ext.isString(events) ? [events] : events;
520 |
521 | for (var i = 0; i < events.length; i++) {
522 | var msg = {
523 | event: events[i],
524 | data: data
525 | };
526 |
527 | me.safeSend(Ext.JSON.encode(msg));
528 | }
529 |
530 | return me;
531 | },
532 |
533 | /**
534 | * @method sendTextMessage
535 | * It sends pure text messages to the server
536 | * @param {String} event Message to send to the server
537 | * @return {Ext.ux.WebSocket} The websocket
538 | * @private
539 | */
540 | sendTextMessage: function (event) {
541 | var me = this;
542 |
543 | me.safeSend(event);
544 |
545 | return me;
546 | }
547 | });
548 |
--------------------------------------------------------------------------------
/WebSocketManager.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Ext.ux.WebSocketManager
3 | * @author Vincenzo Ferrari
4 | * @singleton
5 | *
6 | * Manager of Ext.ux.WebSocket
7 | *
8 | * This singleton provide some useful functions to use for many websockets.
9 | *
10 | * var ws1 = Ext.create ('Ext.ux.WebSocket', {
11 | * url: 'ws://localhost:8888'
12 | * });
13 | *
14 | * Ext.ux.WebSocketManager.register (ws1);
15 | *
16 | * var ws2 = Ext.create ('Ext.ux.WebSocket', {
17 | * url: 'ws://localhost:8900'
18 | * });
19 | *
20 | * Ext.ux.WebSocketManager.register (ws2);
21 | *
22 | * var ws3 = Ext.create ('Ext.ux.WebSocket', {
23 | * url: 'ws://localhost:8950'
24 | * });
25 | *
26 | * Ext.ux.WebSocketManager.register (ws3);
27 | *
28 | * Ext.ux.WebSocketManager.listen ('system shutdown', function (ws, data) {
29 | * Ext.Msg.show ({
30 | * title: 'System Shutdown' ,
31 | * msg: data ,
32 | * icon: Ext.Msg.WARNING ,
33 | * buttons: Ext.Msg.OK
34 | * });
35 | * });
36 | *
37 | * Ext.ux.WebSocketManager.broadcast ('system shutdown', 'BROADCAST: the system will shutdown in few minutes.');
38 | *
39 | * Ext.ux.WebSocketManager.closeAll ();
40 | *
41 | * Ext.ux.WebSocketManager.unregister (ws1);
42 | * Ext.ux.WebSocketManager.unregister (ws2);
43 | * Ext.ux.WebSocketManager.unregister (ws3);
44 | */
45 | Ext.define('Ext.ux.WebSocketManager', {
46 | singleton: true,
47 |
48 | /**
49 | * @property {Ext.util.HashMap} wsList
50 | * @private
51 | */
52 | wsList: Ext.create('Ext.util.HashMap'),
53 |
54 | /**
55 | * @method register
56 | * Registers one or more Ext.ux.WebSocket
57 | * @param {Ext.ux.WebSocket/Ext.ux.WebSocket[]} websockets WebSockets to register. Could be only one.
58 | */
59 | register: function (websockets) {
60 | var me = this;
61 |
62 | // Changes websockets into an array in every case
63 | if (Ext.isObject(websockets)) websockets = [websockets];
64 |
65 | Ext.each(websockets, function (websocket) {
66 | if (!Ext.isEmpty(websocket.url)) me.wsList.add(websocket.url, websocket);
67 | });
68 | },
69 |
70 | /**
71 | * @method contains
72 | * Checks if a websocket is already registered or not
73 | * @param {Ext.ux.WebSocket} websocket The WebSocket to find
74 | * @return {Boolean} True if the websocket is already registered, False otherwise
75 | */
76 | contains: function (websocket) {
77 | return this.wsList.containsKey(websocket.url);
78 | },
79 |
80 | /**
81 | * @method get
82 | * Retrieves a registered websocket by its url
83 | * @param {String} url The url of the websocket to search
84 | * @return {Ext.ux.WebSocket} The websocket or undefined
85 | */
86 | get: function (url) {
87 | return this.wsList.get(url);
88 | },
89 |
90 | /**
91 | * @method each
92 | * Executes a function for each registered websocket
93 | * @param {Function} fn The function to execute
94 | */
95 | each: function (fn) {
96 | this.wsList.each(function (url, websocket, len) {
97 | fn(websocket);
98 | });
99 | },
100 |
101 | /**
102 | * @method unregister
103 | * Unregisters one or more Ext.ux.WebSocket
104 | * @param {Ext.ux.WebSocket/Ext.ux.WebSocket[]} websockets WebSockets to unregister
105 | */
106 | unregister: function (websockets) {
107 | var me = this;
108 |
109 | if (Ext.isObject(websockets)) websockets = [websockets];
110 |
111 | Ext.each(websockets, function (websocket) {
112 | if (me.wsList.containsKey(websocket.url)) me.wsList.removeAtKey(websocket.url);
113 | });
114 | },
115 |
116 | /**
117 | * @method broadcast
118 | * Sends a message to each websocket
119 | * @param {String} event The event to raise
120 | * @param {String/Object} message The data to send
121 | */
122 | broadcast: function (event, message) {
123 | this.multicast([], event, message);
124 | },
125 |
126 | /**
127 | * @method multicast
128 | * Sends a message to each websocket, except those specified
129 | * @param {Ext.ux.WebSocket/Ext.ux.WebSocket[]} websockets An array of websockets to take off the communication
130 | * @param {String} event The event to raise
131 | * @param {String/Object} data The data to send
132 | */
133 | multicast: function (websockets, event, data) {
134 | this.getExcept(websockets).each(function (url, websocket, len) {
135 | if (websocket.isReady()) {
136 | if (Ext.isEmpty(data)) websocket.send(event);
137 | else websocket.send(event, data);
138 | }
139 | });
140 | },
141 |
142 | /**
143 | * @method listen
144 | * Adds an handler for events given to each registered websocket
145 | * @param {String/String[]} events Events to listen
146 | * @param {Function} handler The events' handler
147 | */
148 | listen: function (events, handler) {
149 | if (Ext.isString(events)) events = [events];
150 |
151 | this.wsList.each(function (url, websocket, len) {
152 | Ext.each(events, function (event) {
153 | websocket.on(event, handler);
154 | });
155 | });
156 | },
157 |
158 | /**
159 | * @method listenExcept
160 | * Adds an handler for events given to each registered websocket, except websockets given
161 | * @param {String/String[]} events Events to listen
162 | * @param {Ext.ux.WebSocket/Ext.ux.WebSocket[]} websockets WebSockets to exclude
163 | * @param {Function} handler The events' handler
164 | */
165 | listenExcept: function (events, websockets, handler) {
166 | if (Ext.isString(events)) events = [events];
167 |
168 | this.getExcept(websockets).each(function (url, websocket, len) {
169 | Ext.each(events, function (event) {
170 | websocket.on(event, handler);
171 | });
172 | });
173 | },
174 |
175 | /**
176 | * @method getExcept
177 | * Retrieves registered websockets except the input
178 | * @param {Ext.ux.WebSocket/Ext.ux.WebSocket[]} websockets WebSockets to exclude
179 | * @return {Ext.util.HashMap} Registered websockets except the input
180 | * @private
181 | */
182 | getExcept: function (websockets) {
183 | if (Ext.isObject(websockets)) websockets = [websockets];
184 |
185 | var list = this.wsList.clone();
186 |
187 | // Exclude websockets from the communication
188 | Ext.each(websockets, function (websocket) {
189 | list.removeAtKey(websocket.url);
190 | });
191 |
192 | return list;
193 | },
194 |
195 | /**
196 | * @method closeAll
197 | * Closes any registered websocket
198 | */
199 | closeAll: function () {
200 | var me = this;
201 |
202 | me.wsList.each(function (url, websocket, len) {
203 | websocket.close();
204 | me.unregister(websocket);
205 | });
206 | }
207 | });
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Ext.ux.WebSocket",
3 | "version": "v1.0.0",
4 | "homepage": "https://github.com/wilk/ExtJS-WebSocket",
5 | "authors": [
6 | "Vincenzo (Wilk) Ferrari "
7 | ],
8 | "description": "Ext.ux.WebSocket is an extension to manage HTML5 WebSocket with ExtJS and SenchaTouch",
9 | "main": ["WebSocket.js", "WebSocketManager.js"],
10 | "keywords": [
11 | "extjs",
12 | "senchatouch",
13 | "sencha",
14 | "websocket",
15 | "html5"
16 | ],
17 | "license": "MIT",
18 | "ignore": [
19 | "**/.*",
20 | "node_modules",
21 | "bower_components",
22 | "test",
23 | "tests",
24 | "demo",
25 | "ux",
26 | "package.json",
27 | "bower.json",
28 | "Gruntfile.js",
29 | "*.min.js"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/demo/demo.js:
--------------------------------------------------------------------------------
1 | Ext.Loader.setConfig ({
2 | enabled: true,
3 | paths: {
4 | 'Ext.ux.WebSocket': '../WebSocket.js' ,
5 | 'Ext.ux.WebSocketManager': '../WebSocketManager.js'
6 | }
7 | });
8 |
9 | Ext.require (['Ext.ux.WebSocket', 'Ext.ux.WebSocketManager']);
10 |
11 | Ext.define ('DEMO.view.OpenConnection', {
12 | extend: 'Ext.panel.Panel' ,
13 |
14 | title: 'Open a new connection' ,
15 | width: 300 ,
16 | layout: 'anchor' ,
17 |
18 | openConnection: function (obj) {
19 | var url = obj.up('panel').down('textfield').getValue ();
20 |
21 | var ws = Ext.create ('Ext.ux.WebSocket', {
22 | url: url ,
23 | listeners: {
24 | open: function (ws) {
25 | if (Ext.get(ws.url)) Ext.get(ws.url).dom.innerHTML += '> WebSocket just open! ';
26 | } ,
27 | message: function (ws, data) {
28 | Ext.get(ws.url).dom.innerHTML += '> ' + data + ' ';
29 | } ,
30 | close: function (ws) {
31 | var panel = Ext.getCmp ('panel' + ws.url);
32 |
33 | if ((panel != null) || (panel != undefined)) {
34 | panel.destroy ();
35 | }
36 | }
37 | }
38 | });
39 |
40 | // Connection panel
41 | var panel = Ext.create ('Ext.panel.Panel', {
42 | title: url ,
43 | ws: ws ,
44 | id: 'panel' + url ,
45 |
46 | layout: 'anchor' ,
47 |
48 | bodyPadding: 5 ,
49 | collapsible: true ,
50 |
51 | items: [{
52 | xtype: 'container' ,
53 | html: 'Incoming from the server: '
54 | } , {
55 | xtype: 'textarea' ,
56 | labelAlign: 'top' ,
57 | fieldLabel: 'Send a message' ,
58 | anchor: '100%'
59 | }] ,
60 |
61 | buttons: [{
62 | text: 'Reset' ,
63 | handler: function (btn, evt) {
64 | btn.up('panel').down('textarea').reset ();
65 | }
66 | } , {
67 | text: 'Send' ,
68 | handler: function (btn, evt) {
69 | btn.up('panel').ws.send(btn.up('panel').down('textarea').getValue ());
70 | }
71 | }] ,
72 |
73 | dockedItems: {
74 | xtype: 'toolbar' ,
75 | dock: 'top' ,
76 | defaults: {
77 | xtype: 'button'
78 | } ,
79 | items: [{
80 | // Registers to Ext.ux.WebSocketManager
81 | text: 'Register' ,
82 | handler: function (btn, evt) {
83 | if (btn.getText () === 'Register') {
84 | Ext.ux.WebSocketManager.register (btn.up('toolbar').up('panel').ws);
85 | btn.setText ('Unregister');
86 | }
87 | else {
88 | Ext.ux.WebSocketManager.unregister (btn.up('toolbar').up('panel').ws);
89 | btn.setText ('Register');
90 | }
91 | }
92 | } , {
93 | text: 'Close' ,
94 | handler: function (btn, evt) {
95 | btn.up('toolbar').up('panel').ws.close ();
96 | btn.up('toolbar').up('panel').destroy ();
97 | }
98 | }]
99 | }
100 | });
101 |
102 | Ext.getCmp('connections').add (panel);
103 | } ,
104 |
105 | items: [{
106 | xtype: 'textfield' ,
107 | anchor: '100%' ,
108 | fieldLabel: 'URL' ,
109 | labelAlign: 'top' ,
110 | listeners: {
111 | specialKey: function (tf, evt) {
112 | if (evt.getKey () === evt.ENTER) {
113 | this.up('panel').openConnection (tf);
114 | }
115 | }
116 | }
117 | }] ,
118 |
119 | buttons: [{
120 | text: 'Reset' ,
121 | handler: function (btn, evt) {
122 | btn.up('panel').down('textfield').reset ();
123 | }
124 | } , {
125 | text: 'Open' ,
126 | handler: function (btn) {
127 | btn.up('panel').openConnection (btn);
128 | }
129 | }]
130 | });
131 |
132 | Ext.define ('DEMO.view.BroadcastConnection', {
133 | extend: 'Ext.panel.Panel' ,
134 |
135 | title: 'Broadcast Connection' ,
136 | width: 500 ,
137 | layout: 'fit' ,
138 |
139 | items: [{
140 | xtype: 'textarea' ,
141 | fieldLabel: 'Broadcast a message' ,
142 | labelAlign: 'top' ,
143 | }] ,
144 |
145 | buttons: [{
146 | text: 'Close any connections' ,
147 | handler: function (btn, evt) {
148 | Ext.ux.WebSocketManager.closeAll ();
149 | }
150 | } , '->' , {
151 | text: 'Reset' ,
152 | handler: function (btn, evt) {
153 | btn.up('panel').down('textarea').reset ();
154 | }
155 | } , {
156 | // Broadcasts a message
157 | text: 'Send' ,
158 | handler: function (btn, evt) {
159 | Ext.ux.WebSocketManager.broadcast ('BROADCAST: ' + btn.up('panel').down('textarea').getValue ());
160 | }
161 | }]
162 | });
163 |
164 | Ext.onReady (function () {
165 | var oc = Ext.create ('DEMO.view.OpenConnection');
166 | var bc = Ext.create ('DEMO.view.BroadcastConnection');
167 |
168 | Ext.create ('Ext.container.Container', {
169 | renderTo: Ext.getBody () ,
170 |
171 | layout: {
172 | type: 'hbox' ,
173 | align: 'middle' ,
174 | pack: 'center'
175 | } ,
176 |
177 | items: [{
178 | xtype: 'container' ,
179 | layout: {
180 | type: 'vbox' ,
181 | align: 'stretch'
182 | } ,
183 | width: 800 ,
184 |
185 | items: [{
186 | xtype: 'panel',
187 |
188 | title: 'Demo Ext.ux.WebSocket and Ext.ux.WebSocketManager' ,
189 |
190 | layout: {
191 | type: 'vbox' ,
192 | align: 'stretch'
193 | } ,
194 |
195 | items: [{
196 | xtype: 'container' ,
197 | layout: {
198 | type: 'hbox' ,
199 | align: 'stretch'
200 | } ,
201 | defaults: {
202 | bodyPadding: 5
203 | } ,
204 | items: [oc, bc]
205 | } , {
206 | xtype: 'panel' ,
207 | title: 'Connections' ,
208 | id: 'connections' ,
209 | layout: {
210 | type: 'vbox' ,
211 | align: 'stretch'
212 | }
213 | }]
214 | }]
215 | }]
216 | });
217 | });
218 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ExtJS WebSocket
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/demo/server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | if (process.argv.length < 3) {
4 | console.log('Usage: $ node server.js ');
5 | console.log('Example: $ node server.js 9001 9002 9003');
6 | console.log('Exit');
7 |
8 | process.exit();
9 | }
10 |
11 | var WebSocketServer = require('ws').Server;
12 |
13 | process.argv.forEach(function (val, index) {
14 | if (index > 1) {
15 | var wss = new WebSocketServer({port: val}, function () {
16 | console.log('WebSocketServer :: listening on port ' + val);
17 | });
18 |
19 | wss.on('connection', function (ws) {
20 | console.log('WebSocket[' + val + '] :: connected');
21 |
22 | ws.on('message', function (msg) {
23 | console.log('WebSocket[' + val + '] :: ' + msg);
24 |
25 | ws.send (msg);
26 | });
27 | });
28 | }
29 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Ext.ux.WebSocket",
3 | "version": "0.0.0",
4 | "description": "Ext.ux.WebSocket is an extension to manage HTML5 WebSocket with ExtJS and SenchaTouch",
5 | "main": "WebSocket.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/wilk/ExtJS-WebSocket.git"
12 | },
13 | "keywords": [
14 | "extjs",
15 | "senchatouch",
16 | "websocket",
17 | "html5"
18 | ],
19 | "author": "Vincenzo (Wilk)",
20 | "license": "MIT",
21 | "bugs": {
22 | "url": "https://github.com/wilk/ExtJS-WebSocket/issues"
23 | },
24 | "homepage": "https://github.com/wilk/ExtJS-WebSocket",
25 | "devDependencies": {
26 | "grunt-contrib-jshint": "~0.8.0",
27 | "load-grunt-tasks": "~0.2.1",
28 | "grunt": "~0.4.2",
29 | "grunt-contrib-uglify": "~0.3.0",
30 | "ws": "~0.4.31"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------