├── .gitignore
├── config.json
├── package.json
├── public
├── lib
│ ├── buffer.js
│ └── engine.io.js
├── index.html
├── js
│ ├── game.js
│ └── app.js
└── css
│ └── main.css
├── server.js
├── clients.js
├── README.md
└── main.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "port": 3000
3 | }
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lit-js",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "main.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "node main.js"
9 | },
10 | "author": "Huy",
11 | "license": "MIT",
12 | "dependencies": {
13 | "engine.io": "^1.6.9",
14 | "express": "^4.13.4"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/public/lib/buffer.js:
--------------------------------------------------------------------------------
1 | var BufferUtil = function() {
2 | return {
3 | from: function(str) {
4 | var buf = new ArrayBuffer(str.length*2);
5 | var bufView = new Uint8Array(buf);
6 | for (var i=0, strLen=str.length; i < strLen; i++) {
7 | bufView[i] = str.charCodeAt(i);
8 | }
9 | return buf;
10 | },
11 | toString: function(binary) {
12 | return String.fromCharCode.apply(null, new Uint8Array(binary));
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var Client = require('./clients');
2 |
3 | /*
4 | Main server logic - handle everything game-related here
5 | */
6 |
7 | var Server = function() {
8 |
9 | return {
10 |
11 | connection: function(id) {
12 | console.log('Client', id, 'connected!');
13 | console.log('Total:', Client().count());
14 |
15 | // Send welcome message
16 | Client().get(id).emit('welcome', id);
17 | },
18 | on: function(event, msg) {
19 | console.log('Server received');
20 | console.log('Event:', event, 'Message:', msg);
21 | },
22 | disconnect: function(id) {
23 | console.log('Client', id, 'disconnected!');
24 | console.log('Total:', Client().count());
25 | }
26 |
27 | }
28 |
29 | }
30 |
31 | module.exports = Server;
32 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Your Game
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/clients.js:
--------------------------------------------------------------------------------
1 | global.SOCKET_CLIENTS = {};
2 | var Client = function() {
3 | return {
4 | add: function(socket) {
5 | global.SOCKET_CLIENTS[socket.id] = socket;
6 | },
7 | remove: function(socketId) {
8 | delete global.SOCKET_CLIENTS[socketId];
9 | },
10 | count: function() {
11 | return Object.keys(global.SOCKET_CLIENTS).length;
12 | },
13 | get: function(id) {
14 | if (!global.SOCKET_CLIENTS[id]) return null;
15 | return {
16 | emit: function(eventName, data) {
17 | if (global.SOCKET_CLIENTS[id]) {
18 | global.SOCKET_CLIENTS[id].send(Buffer.from(JSON.stringify({ event: eventName, message: data })));
19 | }
20 | },
21 | broadcast: function(eventName, data) {
22 | Object.keys(global.SOCKET_CLIENTS).each(function(cid) {
23 | if (cid != id) {
24 | global.SOCKET_CLIENTS[cid].send(Buffer.from(JSON.stringify({ event: eventName, message: data })));
25 | }
26 | });
27 | }
28 | }
29 | }
30 | }
31 | }
32 |
33 | module.exports = Client;
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # lit: a better WebSocket game server
2 |
3 |
4 |
5 | **lit** is under development! There are still many things to do, it is not ready for production.
6 |
7 | **Thank you so much [@Ulydev](https://github.com/ulydev) for the awesome logo!**
8 |
9 | ## Demo
10 |
11 | I made a demo game using this library, [check it out](https://github.com/huytd/lit-demo)
12 |
13 | ## Why this?
14 | Few months ago, I built a [cloned version of AgarIO](https://github.com/huytd/agar.io-clone). I'm still not happy with the networking part of the game yet. It still has some bugs, it's laggy and unable to handle the large amount of players. So I decided to quit using Socket.IO and moved to a lower level library called Engine.IO. It focuses on binary data instead of string data, thus improving performance.
15 |
16 | ## Getting started:
17 |
18 | #### Server
19 |
20 | ```
21 | npm install
22 | ```
23 | ```
24 | npm start
25 | ```
26 |
27 | Backend logic is located in ```server.js```
28 |
29 | #### Client
30 |
31 | Navigate to ```http://localhost:3000/```
32 |
33 | Game logic is located in ```public/js/game.js```
34 |
35 | ## License
36 | Published under MIT License.
37 |
--------------------------------------------------------------------------------
/public/js/game.js:
--------------------------------------------------------------------------------
1 | function Game () { };
2 |
3 | Game.prototype.handleNetwork = function(socket) {
4 | //Network callback
5 |
6 | socket.on('open', function() {
7 | socket.on('message', function(data) {
8 | console.log('Received:', BufferUtil().toString(data));
9 | socket.send(BufferUtil().from(JSON.stringify({
10 | event: 'reply',
11 | message: {
12 | text: 'Hey there, server! My name is ' + playerName
13 | }
14 | })));
15 | });
16 | socket.on('close', function(){});
17 | });
18 | }
19 |
20 | Game.prototype.handleLogic = function() {
21 | //Update loop
22 |
23 | console.log('Game updated');
24 | }
25 |
26 | Game.prototype.handleGraphics = function(graphics) {
27 | //Draw loop
28 |
29 | graphics.fillStyle = '#fbfcfc';
30 | graphics.fillRect(0, 0, screenWidth, screenHeight);
31 |
32 | graphics.fillStyle = '#2ecc71';
33 | graphics.strokeStyle = '#27ae60';
34 | graphics.font = 'bold 50px Verdana';
35 | graphics.textAlign = 'center';
36 | graphics.lineWidth = 2;
37 | graphics.fillText('Connected as ' + playerName, screenWidth / 2, screenHeight / 2);
38 | graphics.strokeText('Connected as ' + playerName, screenWidth / 2, screenHeight / 2);
39 | }
40 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | var engine = require('engine.io');
2 | var engineServer = engine.listen(5000);
3 | var http = require('express')();
4 | var Server = require('./server')();
5 | var Client = require('./clients');
6 |
7 | var config = require('./config.json');
8 |
9 | /*
10 | Socket backend - This part should not be edited
11 | */
12 |
13 | engineServer.on('connection', function(socket) {
14 |
15 | if (!Client().get(socket.id)) {
16 | Client().add(socket);
17 | }
18 | Server.connection(socket.id);
19 |
20 | socket.on('message', function(data){
21 | var stringified = data.toString('utf-8').replace(/\0/g, ''); //Take \0 ending character into account
22 | var req = JSON.parse(stringified);
23 | Server.on(req.event, req.message);
24 | });
25 |
26 | socket.on('close', function(){
27 | if (Client().get(socket.id)) {
28 | Client().remove(socket.id);
29 | Server.disconnect(socket.id);
30 | }
31 | });
32 |
33 | });
34 |
35 | // CLIENT
36 |
37 | http.use(require('express').static('public'));
38 |
39 | http.get('/', function(req, res) {
40 | res.sendFile('public/index.html');
41 | });
42 |
43 | var port = process.env.PORT || config.port;
44 | http.listen(3000, function(){
45 | console.log('Server is listening on port 3000');
46 | });
47 |
--------------------------------------------------------------------------------
/public/css/main.css:
--------------------------------------------------------------------------------
1 | * {
2 | font-family: sans-serif;
3 | font-size: 14px;
4 | }
5 |
6 | html, body {
7 | margin: 0; padding: 0;
8 | width: 100%; height: 100%;
9 | background-color: #222;
10 | overflow: hidden;
11 | }
12 |
13 | canvas {
14 | width: 100%; height: 100%;
15 | margin: 0; padding: 0;
16 | }
17 |
18 | #startMenu {
19 | margin:auto;
20 | margin-top:100px;
21 | width: 350px;
22 | padding: 20px;
23 | border-radius: 5px 5px 5px 5px;
24 | -moz-border-radius: 5px 5px 5px 5px;
25 | -webkit-border-radius: 5px 5px 5px 5px;
26 | background-color: white;
27 | box-sizing: border-box;
28 | }
29 |
30 | #startMenu p {
31 | padding: 0;
32 | text-align:center;
33 | font-size: x-large;
34 | font-weight: bold;
35 | }
36 |
37 | #playerNameInput {
38 | width: 100%;
39 | text-align: center;
40 | padding: 10px;
41 | border: solid 1px #dcdcdc;
42 | transition: box-shadow 0.3s, border 0.3s;
43 | box-sizing: border-box;
44 | border-radius: 5px 5px 5px 5px;
45 | -moz-border-radius: 5px 5px 5px 5px;
46 | -webkit-border-radius: 5px 5px 5px 5px;
47 | margin-bottom: 10px;
48 | outline: none;
49 | }
50 |
51 | #playerNameInput:focus, #playerNameInput.focus {
52 | border: solid 1px #CCCCCC;
53 | box-shadow: 0 0 3px 1px #DDDDDD;
54 | }
55 |
56 | #startButton {
57 | position: relative;
58 | margin: auto;
59 | margin-top: 10px;
60 | width: 100%;
61 | height: 40px;
62 | box-sizing: border-box;
63 | font-size: large;
64 | color: white;
65 | text-align: center;
66 | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
67 | background: #2ecc71;
68 | border: 0;
69 | border-bottom: 2px solid #28be68;
70 | cursor: pointer;
71 | -webkit-box-shadow: inset 0 -2px #28be68;
72 | box-shadow: inset 0 -2px #28be68;
73 | border-radius: 5px 5px 5px 5px;
74 | -moz-border-radius: 5px 5px 5px 5px;
75 | -webkit-border-radius: 5px 5px 5px 5px;
76 | margin-bottom: 10px;
77 | }
78 |
79 | #startButton:active, #startButton:hover {
80 | top: 1px;
81 | background: #55D88B;
82 | outline: none;
83 | -webkit-box-shadow: none;
84 | box-shadow: none;
85 | }
86 |
87 | #startMenu h3 {
88 | padding-bottom: 0;
89 | margin-bottom: 0;
90 | }
91 |
92 | #startMenu ul {
93 | margin: 10px; padding: 10px;
94 | margin-top: 0;
95 | }
96 |
97 | #startMenu .input-error {
98 | color : red;
99 | display: none;
100 | font-size : 12px;
101 | }
102 |
--------------------------------------------------------------------------------
/public/js/app.js:
--------------------------------------------------------------------------------
1 | //
2 |
3 | var socket;
4 |
5 | //
6 |
7 | var playerName;
8 |
9 | var btn;
10 | var nickErrorText;
11 | var playerNameInput;
12 |
13 | //
14 |
15 | var screenWidth = window.innerWidth;
16 | var screenHeight = window.innerHeight;
17 |
18 | var c = document.getElementById('cvs');
19 | var canvas = c.getContext('2d');
20 | c.width = screenWidth; c.height = screenHeight;
21 |
22 | var KEY_ENTER = 13;
23 |
24 | //
25 |
26 | var game = new Game();
27 |
28 | //
29 |
30 | function startGame() {
31 | document.getElementById('gameAreaWrapper').style.display = 'block';
32 | document.getElementById('startMenuWrapper').style.display = 'none';
33 |
34 | playerName = playerNameInput.value.replace(/(<([^>]+)>)/ig, '');
35 |
36 | //Set up socket
37 | socket = new eio.Socket('ws://localhost:5000/');
38 | game.handleNetwork(socket);
39 |
40 | //Start loop
41 | windowLoop();
42 | }
43 |
44 | function windowLoop () {
45 | requestAnimFrame(windowLoop);
46 | gameLoop();
47 | }
48 |
49 | function gameLoop () {
50 | game.handleLogic();
51 | game.handleGraphics(canvas);
52 | }
53 |
54 | //Check nick and start game
55 | function checkNick() {
56 | if (validNick()) {
57 | startGame();
58 | } else {
59 | nickErrorText.style.display = 'inline';
60 | }
61 | }
62 |
63 | //Check if nick is alphanumeric
64 | function validNick() {
65 | var regex = /^\w*$/;
66 | console.log('Regex Test', regex.exec(playerNameInput.value));
67 | return regex.exec(playerNameInput.value) !== null;
68 | }
69 |
70 | //Set up form
71 | window.onload = function() {
72 | 'use strict';
73 |
74 | btn = document.getElementById('startButton');
75 | nickErrorText = document.querySelector('#startMenu .input-error');
76 | playerNameInput = document.getElementById('playerNameInput')
77 |
78 | btn.onclick = checkNick; //Check nick on click
79 |
80 | playerNameInput.addEventListener('keypress', function (e) {
81 | var key = e.which || e.keyCode;
82 |
83 | if (key === KEY_ENTER) {
84 | checkNick();
85 | }
86 | });
87 | };
88 |
89 | //Define animation frame
90 | window.requestAnimFrame = (function () {
91 | return window.requestAnimationFrame ||
92 | window.webkitRequestAnimationFrame ||
93 | window.mozRequestAnimationFrame ||
94 | function( callback ){
95 | window.setTimeout(callback, 1000 / 60);
96 | };
97 | })();
98 |
99 | //Resize event
100 | window.addEventListener('resize', function() {
101 | screenWidth = window.innerWidth;
102 | screenHeight = window.innerHeight;
103 | c.width = screenWidth;
104 | c.height = screenHeight;
105 | }, true);
106 |
--------------------------------------------------------------------------------
/public/lib/engine.io.js:
--------------------------------------------------------------------------------
1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.eio=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) {
125 | this.extraHeaders = opts.extraHeaders;
126 | }
127 | }
128 |
129 | this.open();
130 | }
131 |
132 | Socket.priorWebsocketSuccess = false;
133 |
134 | /**
135 | * Mix in `Emitter`.
136 | */
137 |
138 | Emitter(Socket.prototype);
139 |
140 | /**
141 | * Protocol version.
142 | *
143 | * @api public
144 | */
145 |
146 | Socket.protocol = parser.protocol; // this is an int
147 |
148 | /**
149 | * Expose deps for legacy compatibility
150 | * and standalone browser access.
151 | */
152 |
153 | Socket.Socket = Socket;
154 | Socket.Transport = _dereq_('./transport');
155 | Socket.transports = _dereq_('./transports');
156 | Socket.parser = _dereq_('engine.io-parser');
157 |
158 | /**
159 | * Creates transport of the given type.
160 | *
161 | * @param {String} transport name
162 | * @return {Transport}
163 | * @api private
164 | */
165 |
166 | Socket.prototype.createTransport = function (name) {
167 | debug('creating transport "%s"', name);
168 | var query = clone(this.query);
169 |
170 | // append engine.io protocol identifier
171 | query.EIO = parser.protocol;
172 |
173 | // transport name
174 | query.transport = name;
175 |
176 | // session id if we already have one
177 | if (this.id) query.sid = this.id;
178 |
179 | var transport = new transports[name]({
180 | agent: this.agent,
181 | hostname: this.hostname,
182 | port: this.port,
183 | secure: this.secure,
184 | path: this.path,
185 | query: query,
186 | forceJSONP: this.forceJSONP,
187 | jsonp: this.jsonp,
188 | forceBase64: this.forceBase64,
189 | enablesXDR: this.enablesXDR,
190 | timestampRequests: this.timestampRequests,
191 | timestampParam: this.timestampParam,
192 | policyPort: this.policyPort,
193 | socket: this,
194 | pfx: this.pfx,
195 | key: this.key,
196 | passphrase: this.passphrase,
197 | cert: this.cert,
198 | ca: this.ca,
199 | ciphers: this.ciphers,
200 | rejectUnauthorized: this.rejectUnauthorized,
201 | perMessageDeflate: this.perMessageDeflate,
202 | extraHeaders: this.extraHeaders
203 | });
204 |
205 | return transport;
206 | };
207 |
208 | function clone (obj) {
209 | var o = {};
210 | for (var i in obj) {
211 | if (obj.hasOwnProperty(i)) {
212 | o[i] = obj[i];
213 | }
214 | }
215 | return o;
216 | }
217 |
218 | /**
219 | * Initializes transport to use and starts probe.
220 | *
221 | * @api private
222 | */
223 | Socket.prototype.open = function () {
224 | var transport;
225 | if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) {
226 | transport = 'websocket';
227 | } else if (0 === this.transports.length) {
228 | // Emit error on next tick so it can be listened to
229 | var self = this;
230 | setTimeout(function() {
231 | self.emit('error', 'No transports available');
232 | }, 0);
233 | return;
234 | } else {
235 | transport = this.transports[0];
236 | }
237 | this.readyState = 'opening';
238 |
239 | // Retry with the next transport if the transport is disabled (jsonp: false)
240 | try {
241 | transport = this.createTransport(transport);
242 | } catch (e) {
243 | this.transports.shift();
244 | this.open();
245 | return;
246 | }
247 |
248 | transport.open();
249 | this.setTransport(transport);
250 | };
251 |
252 | /**
253 | * Sets the current transport. Disables the existing one (if any).
254 | *
255 | * @api private
256 | */
257 |
258 | Socket.prototype.setTransport = function(transport){
259 | debug('setting transport %s', transport.name);
260 | var self = this;
261 |
262 | if (this.transport) {
263 | debug('clearing existing transport %s', this.transport.name);
264 | this.transport.removeAllListeners();
265 | }
266 |
267 | // set up transport
268 | this.transport = transport;
269 |
270 | // set up transport listeners
271 | transport
272 | .on('drain', function(){
273 | self.onDrain();
274 | })
275 | .on('packet', function(packet){
276 | self.onPacket(packet);
277 | })
278 | .on('error', function(e){
279 | self.onError(e);
280 | })
281 | .on('close', function(){
282 | self.onClose('transport close');
283 | });
284 | };
285 |
286 | /**
287 | * Probes a transport.
288 | *
289 | * @param {String} transport name
290 | * @api private
291 | */
292 |
293 | Socket.prototype.probe = function (name) {
294 | debug('probing transport "%s"', name);
295 | var transport = this.createTransport(name, { probe: 1 })
296 | , failed = false
297 | , self = this;
298 |
299 | Socket.priorWebsocketSuccess = false;
300 |
301 | function onTransportOpen(){
302 | if (self.onlyBinaryUpgrades) {
303 | var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary;
304 | failed = failed || upgradeLosesBinary;
305 | }
306 | if (failed) return;
307 |
308 | debug('probe transport "%s" opened', name);
309 | transport.send([{ type: 'ping', data: 'probe' }]);
310 | transport.once('packet', function (msg) {
311 | if (failed) return;
312 | if ('pong' == msg.type && 'probe' == msg.data) {
313 | debug('probe transport "%s" pong', name);
314 | self.upgrading = true;
315 | self.emit('upgrading', transport);
316 | if (!transport) return;
317 | Socket.priorWebsocketSuccess = 'websocket' == transport.name;
318 |
319 | debug('pausing current transport "%s"', self.transport.name);
320 | self.transport.pause(function () {
321 | if (failed) return;
322 | if ('closed' == self.readyState) return;
323 | debug('changing transport and sending upgrade packet');
324 |
325 | cleanup();
326 |
327 | self.setTransport(transport);
328 | transport.send([{ type: 'upgrade' }]);
329 | self.emit('upgrade', transport);
330 | transport = null;
331 | self.upgrading = false;
332 | self.flush();
333 | });
334 | } else {
335 | debug('probe transport "%s" failed', name);
336 | var err = new Error('probe error');
337 | err.transport = transport.name;
338 | self.emit('upgradeError', err);
339 | }
340 | });
341 | }
342 |
343 | function freezeTransport() {
344 | if (failed) return;
345 |
346 | // Any callback called by transport should be ignored since now
347 | failed = true;
348 |
349 | cleanup();
350 |
351 | transport.close();
352 | transport = null;
353 | }
354 |
355 | //Handle any error that happens while probing
356 | function onerror(err) {
357 | var error = new Error('probe error: ' + err);
358 | error.transport = transport.name;
359 |
360 | freezeTransport();
361 |
362 | debug('probe transport "%s" failed because of error: %s', name, err);
363 |
364 | self.emit('upgradeError', error);
365 | }
366 |
367 | function onTransportClose(){
368 | onerror("transport closed");
369 | }
370 |
371 | //When the socket is closed while we're probing
372 | function onclose(){
373 | onerror("socket closed");
374 | }
375 |
376 | //When the socket is upgraded while we're probing
377 | function onupgrade(to){
378 | if (transport && to.name != transport.name) {
379 | debug('"%s" works - aborting "%s"', to.name, transport.name);
380 | freezeTransport();
381 | }
382 | }
383 |
384 | //Remove all listeners on the transport and on self
385 | function cleanup(){
386 | transport.removeListener('open', onTransportOpen);
387 | transport.removeListener('error', onerror);
388 | transport.removeListener('close', onTransportClose);
389 | self.removeListener('close', onclose);
390 | self.removeListener('upgrading', onupgrade);
391 | }
392 |
393 | transport.once('open', onTransportOpen);
394 | transport.once('error', onerror);
395 | transport.once('close', onTransportClose);
396 |
397 | this.once('close', onclose);
398 | this.once('upgrading', onupgrade);
399 |
400 | transport.open();
401 |
402 | };
403 |
404 | /**
405 | * Called when connection is deemed open.
406 | *
407 | * @api public
408 | */
409 |
410 | Socket.prototype.onOpen = function () {
411 | debug('socket open');
412 | this.readyState = 'open';
413 | Socket.priorWebsocketSuccess = 'websocket' == this.transport.name;
414 | this.emit('open');
415 | this.flush();
416 |
417 | // we check for `readyState` in case an `open`
418 | // listener already closed the socket
419 | if ('open' == this.readyState && this.upgrade && this.transport.pause) {
420 | debug('starting upgrade probes');
421 | for (var i = 0, l = this.upgrades.length; i < l; i++) {
422 | this.probe(this.upgrades[i]);
423 | }
424 | }
425 | };
426 |
427 | /**
428 | * Handles a packet.
429 | *
430 | * @api private
431 | */
432 |
433 | Socket.prototype.onPacket = function (packet) {
434 | if ('opening' == this.readyState || 'open' == this.readyState) {
435 | debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
436 |
437 | this.emit('packet', packet);
438 |
439 | // Socket is live - any packet counts
440 | this.emit('heartbeat');
441 |
442 | switch (packet.type) {
443 | case 'open':
444 | this.onHandshake(parsejson(packet.data));
445 | break;
446 |
447 | case 'pong':
448 | this.setPing();
449 | this.emit('pong');
450 | break;
451 |
452 | case 'error':
453 | var err = new Error('server error');
454 | err.code = packet.data;
455 | this.onError(err);
456 | break;
457 |
458 | case 'message':
459 | this.emit('data', packet.data);
460 | this.emit('message', packet.data);
461 | break;
462 | }
463 | } else {
464 | debug('packet received with socket readyState "%s"', this.readyState);
465 | }
466 | };
467 |
468 | /**
469 | * Called upon handshake completion.
470 | *
471 | * @param {Object} handshake obj
472 | * @api private
473 | */
474 |
475 | Socket.prototype.onHandshake = function (data) {
476 | this.emit('handshake', data);
477 | this.id = data.sid;
478 | this.transport.query.sid = data.sid;
479 | this.upgrades = this.filterUpgrades(data.upgrades);
480 | this.pingInterval = data.pingInterval;
481 | this.pingTimeout = data.pingTimeout;
482 | this.onOpen();
483 | // In case open handler closes socket
484 | if ('closed' == this.readyState) return;
485 | this.setPing();
486 |
487 | // Prolong liveness of socket on heartbeat
488 | this.removeListener('heartbeat', this.onHeartbeat);
489 | this.on('heartbeat', this.onHeartbeat);
490 | };
491 |
492 | /**
493 | * Resets ping timeout.
494 | *
495 | * @api private
496 | */
497 |
498 | Socket.prototype.onHeartbeat = function (timeout) {
499 | clearTimeout(this.pingTimeoutTimer);
500 | var self = this;
501 | self.pingTimeoutTimer = setTimeout(function () {
502 | if ('closed' == self.readyState) return;
503 | self.onClose('ping timeout');
504 | }, timeout || (self.pingInterval + self.pingTimeout));
505 | };
506 |
507 | /**
508 | * Pings server every `this.pingInterval` and expects response
509 | * within `this.pingTimeout` or closes connection.
510 | *
511 | * @api private
512 | */
513 |
514 | Socket.prototype.setPing = function () {
515 | var self = this;
516 | clearTimeout(self.pingIntervalTimer);
517 | self.pingIntervalTimer = setTimeout(function () {
518 | debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
519 | self.ping();
520 | self.onHeartbeat(self.pingTimeout);
521 | }, self.pingInterval);
522 | };
523 |
524 | /**
525 | * Sends a ping packet.
526 | *
527 | * @api private
528 | */
529 |
530 | Socket.prototype.ping = function () {
531 | var self = this;
532 | this.sendPacket('ping', function(){
533 | self.emit('ping');
534 | });
535 | };
536 |
537 | /**
538 | * Called on `drain` event
539 | *
540 | * @api private
541 | */
542 |
543 | Socket.prototype.onDrain = function() {
544 | this.writeBuffer.splice(0, this.prevBufferLen);
545 |
546 | // setting prevBufferLen = 0 is very important
547 | // for example, when upgrading, upgrade packet is sent over,
548 | // and a nonzero prevBufferLen could cause problems on `drain`
549 | this.prevBufferLen = 0;
550 |
551 | if (0 === this.writeBuffer.length) {
552 | this.emit('drain');
553 | } else {
554 | this.flush();
555 | }
556 | };
557 |
558 | /**
559 | * Flush write buffers.
560 | *
561 | * @api private
562 | */
563 |
564 | Socket.prototype.flush = function () {
565 | if ('closed' != this.readyState && this.transport.writable &&
566 | !this.upgrading && this.writeBuffer.length) {
567 | debug('flushing %d packets in socket', this.writeBuffer.length);
568 | this.transport.send(this.writeBuffer);
569 | // keep track of current length of writeBuffer
570 | // splice writeBuffer and callbackBuffer on `drain`
571 | this.prevBufferLen = this.writeBuffer.length;
572 | this.emit('flush');
573 | }
574 | };
575 |
576 | /**
577 | * Sends a message.
578 | *
579 | * @param {String} message.
580 | * @param {Function} callback function.
581 | * @param {Object} options.
582 | * @return {Socket} for chaining.
583 | * @api public
584 | */
585 |
586 | Socket.prototype.write =
587 | Socket.prototype.send = function (msg, options, fn) {
588 | this.sendPacket('message', msg, options, fn);
589 | return this;
590 | };
591 |
592 | /**
593 | * Sends a packet.
594 | *
595 | * @param {String} packet type.
596 | * @param {String} data.
597 | * @param {Object} options.
598 | * @param {Function} callback function.
599 | * @api private
600 | */
601 |
602 | Socket.prototype.sendPacket = function (type, data, options, fn) {
603 | if('function' == typeof data) {
604 | fn = data;
605 | data = undefined;
606 | }
607 |
608 | if ('function' == typeof options) {
609 | fn = options;
610 | options = null;
611 | }
612 |
613 | if ('closing' == this.readyState || 'closed' == this.readyState) {
614 | return;
615 | }
616 |
617 | options = options || {};
618 | options.compress = false !== options.compress;
619 |
620 | var packet = {
621 | type: type,
622 | data: data,
623 | options: options
624 | };
625 | this.emit('packetCreate', packet);
626 | this.writeBuffer.push(packet);
627 | if (fn) this.once('flush', fn);
628 | this.flush();
629 | };
630 |
631 | /**
632 | * Closes the connection.
633 | *
634 | * @api private
635 | */
636 |
637 | Socket.prototype.close = function () {
638 | if ('opening' == this.readyState || 'open' == this.readyState) {
639 | this.readyState = 'closing';
640 |
641 | var self = this;
642 |
643 | if (this.writeBuffer.length) {
644 | this.once('drain', function() {
645 | if (this.upgrading) {
646 | waitForUpgrade();
647 | } else {
648 | close();
649 | }
650 | });
651 | } else if (this.upgrading) {
652 | waitForUpgrade();
653 | } else {
654 | close();
655 | }
656 | }
657 |
658 | function close() {
659 | self.onClose('forced close');
660 | debug('socket closing - telling transport to close');
661 | self.transport.close();
662 | }
663 |
664 | function cleanupAndClose() {
665 | self.removeListener('upgrade', cleanupAndClose);
666 | self.removeListener('upgradeError', cleanupAndClose);
667 | close();
668 | }
669 |
670 | function waitForUpgrade() {
671 | // wait for upgrade to finish since we can't send packets while pausing a transport
672 | self.once('upgrade', cleanupAndClose);
673 | self.once('upgradeError', cleanupAndClose);
674 | }
675 |
676 | return this;
677 | };
678 |
679 | /**
680 | * Called upon transport error
681 | *
682 | * @api private
683 | */
684 |
685 | Socket.prototype.onError = function (err) {
686 | debug('socket error %j', err);
687 | Socket.priorWebsocketSuccess = false;
688 | this.emit('error', err);
689 | this.onClose('transport error', err);
690 | };
691 |
692 | /**
693 | * Called upon transport close.
694 | *
695 | * @api private
696 | */
697 |
698 | Socket.prototype.onClose = function (reason, desc) {
699 | if ('opening' == this.readyState || 'open' == this.readyState || 'closing' == this.readyState) {
700 | debug('socket close with reason: "%s"', reason);
701 | var self = this;
702 |
703 | // clear timers
704 | clearTimeout(this.pingIntervalTimer);
705 | clearTimeout(this.pingTimeoutTimer);
706 |
707 | // stop event from firing again for transport
708 | this.transport.removeAllListeners('close');
709 |
710 | // ensure transport won't stay open
711 | this.transport.close();
712 |
713 | // ignore further transport communication
714 | this.transport.removeAllListeners();
715 |
716 | // set ready state
717 | this.readyState = 'closed';
718 |
719 | // clear session id
720 | this.id = null;
721 |
722 | // emit close event
723 | this.emit('close', reason, desc);
724 |
725 | // clean buffers after, so users can still
726 | // grab the buffers on `close` event
727 | self.writeBuffer = [];
728 | self.prevBufferLen = 0;
729 | }
730 | };
731 |
732 | /**
733 | * Filters upgrades, returning only those matching client transports.
734 | *
735 | * @param {Array} server upgrades
736 | * @api private
737 | *
738 | */
739 |
740 | Socket.prototype.filterUpgrades = function (upgrades) {
741 | var filteredUpgrades = [];
742 | for (var i = 0, j = upgrades.length; i';
1169 | iframe = document.createElement(html);
1170 | } catch (e) {
1171 | iframe = document.createElement('iframe');
1172 | iframe.name = self.iframeId;
1173 | iframe.src = 'javascript:0';
1174 | }
1175 |
1176 | iframe.id = self.iframeId;
1177 |
1178 | self.form.appendChild(iframe);
1179 | self.iframe = iframe;
1180 | }
1181 |
1182 | initIframe();
1183 |
1184 | // escape \n to prevent it from being converted into \r\n by some UAs
1185 | // double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side
1186 | data = data.replace(rEscapedNewline, '\\\n');
1187 | this.area.value = data.replace(rNewline, '\\n');
1188 |
1189 | try {
1190 | this.form.submit();
1191 | } catch(e) {}
1192 |
1193 | if (this.iframe.attachEvent) {
1194 | this.iframe.onreadystatechange = function(){
1195 | if (self.iframe.readyState == 'complete') {
1196 | complete();
1197 | }
1198 | };
1199 | } else {
1200 | this.iframe.onload = complete;
1201 | }
1202 | };
1203 |
1204 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {})
1205 | },{"./polling":8,"component-inherit":17}],7:[function(_dereq_,module,exports){
1206 | (function (global){
1207 | /**
1208 | * Module requirements.
1209 | */
1210 |
1211 | var XMLHttpRequest = _dereq_('xmlhttprequest-ssl');
1212 | var Polling = _dereq_('./polling');
1213 | var Emitter = _dereq_('component-emitter');
1214 | var inherit = _dereq_('component-inherit');
1215 | var debug = _dereq_('debug')('engine.io-client:polling-xhr');
1216 |
1217 | /**
1218 | * Module exports.
1219 | */
1220 |
1221 | module.exports = XHR;
1222 | module.exports.Request = Request;
1223 |
1224 | /**
1225 | * Empty function
1226 | */
1227 |
1228 | function empty(){}
1229 |
1230 | /**
1231 | * XHR Polling constructor.
1232 | *
1233 | * @param {Object} opts
1234 | * @api public
1235 | */
1236 |
1237 | function XHR(opts){
1238 | Polling.call(this, opts);
1239 |
1240 | if (global.location) {
1241 | var isSSL = 'https:' == location.protocol;
1242 | var port = location.port;
1243 |
1244 | // some user agents have empty `location.port`
1245 | if (!port) {
1246 | port = isSSL ? 443 : 80;
1247 | }
1248 |
1249 | this.xd = opts.hostname != global.location.hostname ||
1250 | port != opts.port;
1251 | this.xs = opts.secure != isSSL;
1252 | } else {
1253 | this.extraHeaders = opts.extraHeaders;
1254 | }
1255 | }
1256 |
1257 | /**
1258 | * Inherits from Polling.
1259 | */
1260 |
1261 | inherit(XHR, Polling);
1262 |
1263 | /**
1264 | * XHR supports binary
1265 | */
1266 |
1267 | XHR.prototype.supportsBinary = true;
1268 |
1269 | /**
1270 | * Creates a request.
1271 | *
1272 | * @param {String} method
1273 | * @api private
1274 | */
1275 |
1276 | XHR.prototype.request = function(opts){
1277 | opts = opts || {};
1278 | opts.uri = this.uri();
1279 | opts.xd = this.xd;
1280 | opts.xs = this.xs;
1281 | opts.agent = this.agent || false;
1282 | opts.supportsBinary = this.supportsBinary;
1283 | opts.enablesXDR = this.enablesXDR;
1284 |
1285 | // SSL options for Node.js client
1286 | opts.pfx = this.pfx;
1287 | opts.key = this.key;
1288 | opts.passphrase = this.passphrase;
1289 | opts.cert = this.cert;
1290 | opts.ca = this.ca;
1291 | opts.ciphers = this.ciphers;
1292 | opts.rejectUnauthorized = this.rejectUnauthorized;
1293 |
1294 | // other options for Node.js client
1295 | opts.extraHeaders = this.extraHeaders;
1296 |
1297 | return new Request(opts);
1298 | };
1299 |
1300 | /**
1301 | * Sends data.
1302 | *
1303 | * @param {String} data to send.
1304 | * @param {Function} called upon flush.
1305 | * @api private
1306 | */
1307 |
1308 | XHR.prototype.doWrite = function(data, fn){
1309 | var isBinary = typeof data !== 'string' && data !== undefined;
1310 | var req = this.request({ method: 'POST', data: data, isBinary: isBinary });
1311 | var self = this;
1312 | req.on('success', fn);
1313 | req.on('error', function(err){
1314 | self.onError('xhr post error', err);
1315 | });
1316 | this.sendXhr = req;
1317 | };
1318 |
1319 | /**
1320 | * Starts a poll cycle.
1321 | *
1322 | * @api private
1323 | */
1324 |
1325 | XHR.prototype.doPoll = function(){
1326 | debug('xhr poll');
1327 | var req = this.request();
1328 | var self = this;
1329 | req.on('data', function(data){
1330 | self.onData(data);
1331 | });
1332 | req.on('error', function(err){
1333 | self.onError('xhr poll error', err);
1334 | });
1335 | this.pollXhr = req;
1336 | };
1337 |
1338 | /**
1339 | * Request constructor
1340 | *
1341 | * @param {Object} options
1342 | * @api public
1343 | */
1344 |
1345 | function Request(opts){
1346 | this.method = opts.method || 'GET';
1347 | this.uri = opts.uri;
1348 | this.xd = !!opts.xd;
1349 | this.xs = !!opts.xs;
1350 | this.async = false !== opts.async;
1351 | this.data = undefined != opts.data ? opts.data : null;
1352 | this.agent = opts.agent;
1353 | this.isBinary = opts.isBinary;
1354 | this.supportsBinary = opts.supportsBinary;
1355 | this.enablesXDR = opts.enablesXDR;
1356 |
1357 | // SSL options for Node.js client
1358 | this.pfx = opts.pfx;
1359 | this.key = opts.key;
1360 | this.passphrase = opts.passphrase;
1361 | this.cert = opts.cert;
1362 | this.ca = opts.ca;
1363 | this.ciphers = opts.ciphers;
1364 | this.rejectUnauthorized = opts.rejectUnauthorized;
1365 |
1366 | // other options for Node.js client
1367 | this.extraHeaders = opts.extraHeaders;
1368 |
1369 | this.create();
1370 | }
1371 |
1372 | /**
1373 | * Mix in `Emitter`.
1374 | */
1375 |
1376 | Emitter(Request.prototype);
1377 |
1378 | /**
1379 | * Creates the XHR object and sends the request.
1380 | *
1381 | * @api private
1382 | */
1383 |
1384 | Request.prototype.create = function(){
1385 | var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR };
1386 |
1387 | // SSL options for Node.js client
1388 | opts.pfx = this.pfx;
1389 | opts.key = this.key;
1390 | opts.passphrase = this.passphrase;
1391 | opts.cert = this.cert;
1392 | opts.ca = this.ca;
1393 | opts.ciphers = this.ciphers;
1394 | opts.rejectUnauthorized = this.rejectUnauthorized;
1395 |
1396 | var xhr = this.xhr = new XMLHttpRequest(opts);
1397 | var self = this;
1398 |
1399 | try {
1400 | debug('xhr open %s: %s', this.method, this.uri);
1401 | xhr.open(this.method, this.uri, this.async);
1402 | try {
1403 | if (this.extraHeaders) {
1404 | xhr.setDisableHeaderCheck(true);
1405 | for (var i in this.extraHeaders) {
1406 | if (this.extraHeaders.hasOwnProperty(i)) {
1407 | xhr.setRequestHeader(i, this.extraHeaders[i]);
1408 | }
1409 | }
1410 | }
1411 | } catch (e) {}
1412 | if (this.supportsBinary) {
1413 | // This has to be done after open because Firefox is stupid
1414 | // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension
1415 | xhr.responseType = 'arraybuffer';
1416 | }
1417 |
1418 | if ('POST' == this.method) {
1419 | try {
1420 | if (this.isBinary) {
1421 | xhr.setRequestHeader('Content-type', 'application/octet-stream');
1422 | } else {
1423 | xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
1424 | }
1425 | } catch (e) {}
1426 | }
1427 |
1428 | // ie6 check
1429 | if ('withCredentials' in xhr) {
1430 | xhr.withCredentials = true;
1431 | }
1432 |
1433 | if (this.hasXDR()) {
1434 | xhr.onload = function(){
1435 | self.onLoad();
1436 | };
1437 | xhr.onerror = function(){
1438 | self.onError(xhr.responseText);
1439 | };
1440 | } else {
1441 | xhr.onreadystatechange = function(){
1442 | if (4 != xhr.readyState) return;
1443 | if (200 == xhr.status || 1223 == xhr.status) {
1444 | self.onLoad();
1445 | } else {
1446 | // make sure the `error` event handler that's user-set
1447 | // does not throw in the same tick and gets caught here
1448 | setTimeout(function(){
1449 | self.onError(xhr.status);
1450 | }, 0);
1451 | }
1452 | };
1453 | }
1454 |
1455 | debug('xhr data %s', this.data);
1456 | xhr.send(this.data);
1457 | } catch (e) {
1458 | // Need to defer since .create() is called directly fhrom the constructor
1459 | // and thus the 'error' event can only be only bound *after* this exception
1460 | // occurs. Therefore, also, we cannot throw here at all.
1461 | setTimeout(function() {
1462 | self.onError(e);
1463 | }, 0);
1464 | return;
1465 | }
1466 |
1467 | if (global.document) {
1468 | this.index = Request.requestsCount++;
1469 | Request.requests[this.index] = this;
1470 | }
1471 | };
1472 |
1473 | /**
1474 | * Called upon successful response.
1475 | *
1476 | * @api private
1477 | */
1478 |
1479 | Request.prototype.onSuccess = function(){
1480 | this.emit('success');
1481 | this.cleanup();
1482 | };
1483 |
1484 | /**
1485 | * Called if we have data.
1486 | *
1487 | * @api private
1488 | */
1489 |
1490 | Request.prototype.onData = function(data){
1491 | this.emit('data', data);
1492 | this.onSuccess();
1493 | };
1494 |
1495 | /**
1496 | * Called upon error.
1497 | *
1498 | * @api private
1499 | */
1500 |
1501 | Request.prototype.onError = function(err){
1502 | this.emit('error', err);
1503 | this.cleanup(true);
1504 | };
1505 |
1506 | /**
1507 | * Cleans up house.
1508 | *
1509 | * @api private
1510 | */
1511 |
1512 | Request.prototype.cleanup = function(fromError){
1513 | if ('undefined' == typeof this.xhr || null === this.xhr) {
1514 | return;
1515 | }
1516 | // xmlhttprequest
1517 | if (this.hasXDR()) {
1518 | this.xhr.onload = this.xhr.onerror = empty;
1519 | } else {
1520 | this.xhr.onreadystatechange = empty;
1521 | }
1522 |
1523 | if (fromError) {
1524 | try {
1525 | this.xhr.abort();
1526 | } catch(e) {}
1527 | }
1528 |
1529 | if (global.document) {
1530 | delete Request.requests[this.index];
1531 | }
1532 |
1533 | this.xhr = null;
1534 | };
1535 |
1536 | /**
1537 | * Called upon load.
1538 | *
1539 | * @api private
1540 | */
1541 |
1542 | Request.prototype.onLoad = function(){
1543 | var data;
1544 | try {
1545 | var contentType;
1546 | try {
1547 | contentType = this.xhr.getResponseHeader('Content-Type').split(';')[0];
1548 | } catch (e) {}
1549 | if (contentType === 'application/octet-stream') {
1550 | data = this.xhr.response;
1551 | } else {
1552 | if (!this.supportsBinary) {
1553 | data = this.xhr.responseText;
1554 | } else {
1555 | try {
1556 | data = String.fromCharCode.apply(null, new Uint8Array(this.xhr.response));
1557 | } catch (e) {
1558 | var ui8Arr = new Uint8Array(this.xhr.response);
1559 | var dataArray = [];
1560 | for (var idx = 0, length = ui8Arr.length; idx < length; idx++) {
1561 | dataArray.push(ui8Arr[idx]);
1562 | }
1563 |
1564 | data = String.fromCharCode.apply(null, dataArray);
1565 | }
1566 | }
1567 | }
1568 | } catch (e) {
1569 | this.onError(e);
1570 | }
1571 | if (null != data) {
1572 | this.onData(data);
1573 | }
1574 | };
1575 |
1576 | /**
1577 | * Check if it has XDomainRequest.
1578 | *
1579 | * @api private
1580 | */
1581 |
1582 | Request.prototype.hasXDR = function(){
1583 | return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR;
1584 | };
1585 |
1586 | /**
1587 | * Aborts the request.
1588 | *
1589 | * @api public
1590 | */
1591 |
1592 | Request.prototype.abort = function(){
1593 | this.cleanup();
1594 | };
1595 |
1596 | /**
1597 | * Aborts pending requests when unloading the window. This is needed to prevent
1598 | * memory leaks (e.g. when using IE) and to ensure that no spurious error is
1599 | * emitted.
1600 | */
1601 |
1602 | if (global.document) {
1603 | Request.requestsCount = 0;
1604 | Request.requests = {};
1605 | if (global.attachEvent) {
1606 | global.attachEvent('onunload', unloadHandler);
1607 | } else if (global.addEventListener) {
1608 | global.addEventListener('beforeunload', unloadHandler, false);
1609 | }
1610 | }
1611 |
1612 | function unloadHandler() {
1613 | for (var i in Request.requests) {
1614 | if (Request.requests.hasOwnProperty(i)) {
1615 | Request.requests[i].abort();
1616 | }
1617 | }
1618 | }
1619 |
1620 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {})
1621 | },{"./polling":8,"component-emitter":16,"component-inherit":17,"debug":18,"xmlhttprequest-ssl":10}],8:[function(_dereq_,module,exports){
1622 | /**
1623 | * Module dependencies.
1624 | */
1625 |
1626 | var Transport = _dereq_('../transport');
1627 | var parseqs = _dereq_('parseqs');
1628 | var parser = _dereq_('engine.io-parser');
1629 | var inherit = _dereq_('component-inherit');
1630 | var yeast = _dereq_('yeast');
1631 | var debug = _dereq_('debug')('engine.io-client:polling');
1632 |
1633 | /**
1634 | * Module exports.
1635 | */
1636 |
1637 | module.exports = Polling;
1638 |
1639 | /**
1640 | * Is XHR2 supported?
1641 | */
1642 |
1643 | var hasXHR2 = (function() {
1644 | var XMLHttpRequest = _dereq_('xmlhttprequest-ssl');
1645 | var xhr = new XMLHttpRequest({ xdomain: false });
1646 | return null != xhr.responseType;
1647 | })();
1648 |
1649 | /**
1650 | * Polling interface.
1651 | *
1652 | * @param {Object} opts
1653 | * @api private
1654 | */
1655 |
1656 | function Polling(opts){
1657 | var forceBase64 = (opts && opts.forceBase64);
1658 | if (!hasXHR2 || forceBase64) {
1659 | this.supportsBinary = false;
1660 | }
1661 | Transport.call(this, opts);
1662 | }
1663 |
1664 | /**
1665 | * Inherits from Transport.
1666 | */
1667 |
1668 | inherit(Polling, Transport);
1669 |
1670 | /**
1671 | * Transport name.
1672 | */
1673 |
1674 | Polling.prototype.name = 'polling';
1675 |
1676 | /**
1677 | * Opens the socket (triggers polling). We write a PING message to determine
1678 | * when the transport is open.
1679 | *
1680 | * @api private
1681 | */
1682 |
1683 | Polling.prototype.doOpen = function(){
1684 | this.poll();
1685 | };
1686 |
1687 | /**
1688 | * Pauses polling.
1689 | *
1690 | * @param {Function} callback upon buffers are flushed and transport is paused
1691 | * @api private
1692 | */
1693 |
1694 | Polling.prototype.pause = function(onPause){
1695 | var pending = 0;
1696 | var self = this;
1697 |
1698 | this.readyState = 'pausing';
1699 |
1700 | function pause(){
1701 | debug('paused');
1702 | self.readyState = 'paused';
1703 | onPause();
1704 | }
1705 |
1706 | if (this.polling || !this.writable) {
1707 | var total = 0;
1708 |
1709 | if (this.polling) {
1710 | debug('we are currently polling - waiting to pause');
1711 | total++;
1712 | this.once('pollComplete', function(){
1713 | debug('pre-pause polling complete');
1714 | --total || pause();
1715 | });
1716 | }
1717 |
1718 | if (!this.writable) {
1719 | debug('we are currently writing - waiting to pause');
1720 | total++;
1721 | this.once('drain', function(){
1722 | debug('pre-pause writing complete');
1723 | --total || pause();
1724 | });
1725 | }
1726 | } else {
1727 | pause();
1728 | }
1729 | };
1730 |
1731 | /**
1732 | * Starts polling cycle.
1733 | *
1734 | * @api public
1735 | */
1736 |
1737 | Polling.prototype.poll = function(){
1738 | debug('polling');
1739 | this.polling = true;
1740 | this.doPoll();
1741 | this.emit('poll');
1742 | };
1743 |
1744 | /**
1745 | * Overloads onData to detect payloads.
1746 | *
1747 | * @api private
1748 | */
1749 |
1750 | Polling.prototype.onData = function(data){
1751 | var self = this;
1752 | debug('polling got data %s', data);
1753 | var callback = function(packet, index, total) {
1754 | // if its the first message we consider the transport open
1755 | if ('opening' == self.readyState) {
1756 | self.onOpen();
1757 | }
1758 |
1759 | // if its a close packet, we close the ongoing requests
1760 | if ('close' == packet.type) {
1761 | self.onClose();
1762 | return false;
1763 | }
1764 |
1765 | // otherwise bypass onData and handle the message
1766 | self.onPacket(packet);
1767 | };
1768 |
1769 | // decode payload
1770 | parser.decodePayload(data, this.socket.binaryType, callback);
1771 |
1772 | // if an event did not trigger closing
1773 | if ('closed' != this.readyState) {
1774 | // if we got data we're not polling
1775 | this.polling = false;
1776 | this.emit('pollComplete');
1777 |
1778 | if ('open' == this.readyState) {
1779 | this.poll();
1780 | } else {
1781 | debug('ignoring poll - transport state "%s"', this.readyState);
1782 | }
1783 | }
1784 | };
1785 |
1786 | /**
1787 | * For polling, send a close packet.
1788 | *
1789 | * @api private
1790 | */
1791 |
1792 | Polling.prototype.doClose = function(){
1793 | var self = this;
1794 |
1795 | function close(){
1796 | debug('writing close packet');
1797 | self.write([{ type: 'close' }]);
1798 | }
1799 |
1800 | if ('open' == this.readyState) {
1801 | debug('transport open - closing');
1802 | close();
1803 | } else {
1804 | // in case we're trying to close while
1805 | // handshaking is in progress (GH-164)
1806 | debug('transport not open - deferring close');
1807 | this.once('open', close);
1808 | }
1809 | };
1810 |
1811 | /**
1812 | * Writes a packets payload.
1813 | *
1814 | * @param {Array} data packets
1815 | * @param {Function} drain callback
1816 | * @api private
1817 | */
1818 |
1819 | Polling.prototype.write = function(packets){
1820 | var self = this;
1821 | this.writable = false;
1822 | var callbackfn = function() {
1823 | self.writable = true;
1824 | self.emit('drain');
1825 | };
1826 |
1827 | var self = this;
1828 | parser.encodePayload(packets, this.supportsBinary, function(data) {
1829 | self.doWrite(data, callbackfn);
1830 | });
1831 | };
1832 |
1833 | /**
1834 | * Generates uri for connection.
1835 | *
1836 | * @api private
1837 | */
1838 |
1839 | Polling.prototype.uri = function(){
1840 | var query = this.query || {};
1841 | var schema = this.secure ? 'https' : 'http';
1842 | var port = '';
1843 |
1844 | // cache busting is forced
1845 | if (false !== this.timestampRequests) {
1846 | query[this.timestampParam] = yeast();
1847 | }
1848 |
1849 | if (!this.supportsBinary && !query.sid) {
1850 | query.b64 = 1;
1851 | }
1852 |
1853 | query = parseqs.encode(query);
1854 |
1855 | // avoid port if default for schema
1856 | if (this.port && (('https' == schema && this.port != 443) ||
1857 | ('http' == schema && this.port != 80))) {
1858 | port = ':' + this.port;
1859 | }
1860 |
1861 | // prepend ? to query
1862 | if (query.length) {
1863 | query = '?' + query;
1864 | }
1865 |
1866 | var ipv6 = this.hostname.indexOf(':') !== -1;
1867 | return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
1868 | };
1869 |
1870 | },{"../transport":4,"component-inherit":17,"debug":18,"engine.io-parser":20,"parseqs":28,"xmlhttprequest-ssl":10,"yeast":31}],9:[function(_dereq_,module,exports){
1871 | (function (global){
1872 | /**
1873 | * Module dependencies.
1874 | */
1875 |
1876 | var Transport = _dereq_('../transport');
1877 | var parser = _dereq_('engine.io-parser');
1878 | var parseqs = _dereq_('parseqs');
1879 | var inherit = _dereq_('component-inherit');
1880 | var yeast = _dereq_('yeast');
1881 | var debug = _dereq_('debug')('engine.io-client:websocket');
1882 | var BrowserWebSocket = global.WebSocket || global.MozWebSocket;
1883 |
1884 | /**
1885 | * Get either the `WebSocket` or `MozWebSocket` globals
1886 | * in the browser or try to resolve WebSocket-compatible
1887 | * interface exposed by `ws` for Node-like environment.
1888 | */
1889 |
1890 | var WebSocket = BrowserWebSocket;
1891 | if (!WebSocket && typeof window === 'undefined') {
1892 | try {
1893 | WebSocket = _dereq_('ws');
1894 | } catch (e) { }
1895 | }
1896 |
1897 | /**
1898 | * Module exports.
1899 | */
1900 |
1901 | module.exports = WS;
1902 |
1903 | /**
1904 | * WebSocket transport constructor.
1905 | *
1906 | * @api {Object} connection options
1907 | * @api public
1908 | */
1909 |
1910 | function WS(opts){
1911 | var forceBase64 = (opts && opts.forceBase64);
1912 | if (forceBase64) {
1913 | this.supportsBinary = false;
1914 | }
1915 | this.perMessageDeflate = opts.perMessageDeflate;
1916 | Transport.call(this, opts);
1917 | }
1918 |
1919 | /**
1920 | * Inherits from Transport.
1921 | */
1922 |
1923 | inherit(WS, Transport);
1924 |
1925 | /**
1926 | * Transport name.
1927 | *
1928 | * @api public
1929 | */
1930 |
1931 | WS.prototype.name = 'websocket';
1932 |
1933 | /*
1934 | * WebSockets support binary
1935 | */
1936 |
1937 | WS.prototype.supportsBinary = true;
1938 |
1939 | /**
1940 | * Opens socket.
1941 | *
1942 | * @api private
1943 | */
1944 |
1945 | WS.prototype.doOpen = function(){
1946 | if (!this.check()) {
1947 | // let probe timeout
1948 | return;
1949 | }
1950 |
1951 | var self = this;
1952 | var uri = this.uri();
1953 | var protocols = void(0);
1954 | var opts = {
1955 | agent: this.agent,
1956 | perMessageDeflate: this.perMessageDeflate
1957 | };
1958 |
1959 | // SSL options for Node.js client
1960 | opts.pfx = this.pfx;
1961 | opts.key = this.key;
1962 | opts.passphrase = this.passphrase;
1963 | opts.cert = this.cert;
1964 | opts.ca = this.ca;
1965 | opts.ciphers = this.ciphers;
1966 | opts.rejectUnauthorized = this.rejectUnauthorized;
1967 | if (this.extraHeaders) {
1968 | opts.headers = this.extraHeaders;
1969 | }
1970 |
1971 | this.ws = BrowserWebSocket ? new WebSocket(uri) : new WebSocket(uri, protocols, opts);
1972 |
1973 | if (this.ws.binaryType === undefined) {
1974 | this.supportsBinary = false;
1975 | }
1976 |
1977 | if (this.ws.supports && this.ws.supports.binary) {
1978 | this.supportsBinary = true;
1979 | this.ws.binaryType = 'buffer';
1980 | } else {
1981 | this.ws.binaryType = 'arraybuffer';
1982 | }
1983 |
1984 | this.addEventListeners();
1985 | };
1986 |
1987 | /**
1988 | * Adds event listeners to the socket
1989 | *
1990 | * @api private
1991 | */
1992 |
1993 | WS.prototype.addEventListeners = function(){
1994 | var self = this;
1995 |
1996 | this.ws.onopen = function(){
1997 | self.onOpen();
1998 | };
1999 | this.ws.onclose = function(){
2000 | self.onClose();
2001 | };
2002 | this.ws.onmessage = function(ev){
2003 | self.onData(ev.data);
2004 | };
2005 | this.ws.onerror = function(e){
2006 | self.onError('websocket error', e);
2007 | };
2008 | };
2009 |
2010 | /**
2011 | * Override `onData` to use a timer on iOS.
2012 | * See: https://gist.github.com/mloughran/2052006
2013 | *
2014 | * @api private
2015 | */
2016 |
2017 | if ('undefined' != typeof navigator
2018 | && /iPad|iPhone|iPod/i.test(navigator.userAgent)) {
2019 | WS.prototype.onData = function(data){
2020 | var self = this;
2021 | setTimeout(function(){
2022 | Transport.prototype.onData.call(self, data);
2023 | }, 0);
2024 | };
2025 | }
2026 |
2027 | /**
2028 | * Writes data to socket.
2029 | *
2030 | * @param {Array} array of packets.
2031 | * @api private
2032 | */
2033 |
2034 | WS.prototype.write = function(packets){
2035 | var self = this;
2036 | this.writable = false;
2037 |
2038 | // encodePacket efficient as it uses WS framing
2039 | // no need for encodePayload
2040 | var total = packets.length;
2041 | for (var i = 0, l = total; i < l; i++) {
2042 | (function(packet) {
2043 | parser.encodePacket(packet, self.supportsBinary, function(data) {
2044 | if (!BrowserWebSocket) {
2045 | // always create a new object (GH-437)
2046 | var opts = {};
2047 | if (packet.options) {
2048 | opts.compress = packet.options.compress;
2049 | }
2050 |
2051 | if (self.perMessageDeflate) {
2052 | var len = 'string' == typeof data ? global.Buffer.byteLength(data) : data.length;
2053 | if (len < self.perMessageDeflate.threshold) {
2054 | opts.compress = false;
2055 | }
2056 | }
2057 | }
2058 |
2059 | //Sometimes the websocket has already been closed but the browser didn't
2060 | //have a chance of informing us about it yet, in that case send will
2061 | //throw an error
2062 | try {
2063 | if (BrowserWebSocket) {
2064 | // TypeError is thrown when passing the second argument on Safari
2065 | self.ws.send(data);
2066 | } else {
2067 | self.ws.send(data, opts);
2068 | }
2069 | } catch (e){
2070 | debug('websocket closed before onclose event');
2071 | }
2072 |
2073 | --total || done();
2074 | });
2075 | })(packets[i]);
2076 | }
2077 |
2078 | function done(){
2079 | self.emit('flush');
2080 |
2081 | // fake drain
2082 | // defer to next tick to allow Socket to clear writeBuffer
2083 | setTimeout(function(){
2084 | self.writable = true;
2085 | self.emit('drain');
2086 | }, 0);
2087 | }
2088 | };
2089 |
2090 | /**
2091 | * Called upon close
2092 | *
2093 | * @api private
2094 | */
2095 |
2096 | WS.prototype.onClose = function(){
2097 | Transport.prototype.onClose.call(this);
2098 | };
2099 |
2100 | /**
2101 | * Closes socket.
2102 | *
2103 | * @api private
2104 | */
2105 |
2106 | WS.prototype.doClose = function(){
2107 | if (typeof this.ws !== 'undefined') {
2108 | this.ws.close();
2109 | }
2110 | };
2111 |
2112 | /**
2113 | * Generates uri for connection.
2114 | *
2115 | * @api private
2116 | */
2117 |
2118 | WS.prototype.uri = function(){
2119 | var query = this.query || {};
2120 | var schema = this.secure ? 'wss' : 'ws';
2121 | var port = '';
2122 |
2123 | // avoid port if default for schema
2124 | if (this.port && (('wss' == schema && this.port != 443)
2125 | || ('ws' == schema && this.port != 80))) {
2126 | port = ':' + this.port;
2127 | }
2128 |
2129 | // append timestamp to URI
2130 | if (this.timestampRequests) {
2131 | query[this.timestampParam] = yeast();
2132 | }
2133 |
2134 | // communicate binary support capabilities
2135 | if (!this.supportsBinary) {
2136 | query.b64 = 1;
2137 | }
2138 |
2139 | query = parseqs.encode(query);
2140 |
2141 | // prepend ? to query
2142 | if (query.length) {
2143 | query = '?' + query;
2144 | }
2145 |
2146 | var ipv6 = this.hostname.indexOf(':') !== -1;
2147 | return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
2148 | };
2149 |
2150 | /**
2151 | * Feature detection for WebSocket.
2152 | *
2153 | * @return {Boolean} whether this transport is available.
2154 | * @api public
2155 | */
2156 |
2157 | WS.prototype.check = function(){
2158 | return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name);
2159 | };
2160 |
2161 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {})
2162 | },{"../transport":4,"component-inherit":17,"debug":18,"engine.io-parser":20,"parseqs":28,"ws":15,"yeast":31}],10:[function(_dereq_,module,exports){
2163 | // browser shim for xmlhttprequest module
2164 | var hasCORS = _dereq_('has-cors');
2165 |
2166 | module.exports = function(opts) {
2167 | var xdomain = opts.xdomain;
2168 |
2169 | // scheme must be same when usign XDomainRequest
2170 | // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
2171 | var xscheme = opts.xscheme;
2172 |
2173 | // XDomainRequest has a flow of not sending cookie, therefore it should be disabled as a default.
2174 | // https://github.com/Automattic/engine.io-client/pull/217
2175 | var enablesXDR = opts.enablesXDR;
2176 |
2177 | // XMLHttpRequest can be disabled on IE
2178 | try {
2179 | if ('undefined' != typeof XMLHttpRequest && (!xdomain || hasCORS)) {
2180 | return new XMLHttpRequest();
2181 | }
2182 | } catch (e) { }
2183 |
2184 | // Use XDomainRequest for IE8 if enablesXDR is true
2185 | // because loading bar keeps flashing when using jsonp-polling
2186 | // https://github.com/yujiosaka/socke.io-ie8-loading-example
2187 | try {
2188 | if ('undefined' != typeof XDomainRequest && !xscheme && enablesXDR) {
2189 | return new XDomainRequest();
2190 | }
2191 | } catch (e) { }
2192 |
2193 | if (!xdomain) {
2194 | try {
2195 | return new ActiveXObject('Microsoft.XMLHTTP');
2196 | } catch(e) { }
2197 | }
2198 | }
2199 |
2200 | },{"has-cors":23}],11:[function(_dereq_,module,exports){
2201 | module.exports = after
2202 |
2203 | function after(count, callback, err_cb) {
2204 | var bail = false
2205 | err_cb = err_cb || noop
2206 | proxy.count = count
2207 |
2208 | return (count === 0) ? callback() : proxy
2209 |
2210 | function proxy(err, result) {
2211 | if (proxy.count <= 0) {
2212 | throw new Error('after called too many times')
2213 | }
2214 | --proxy.count
2215 |
2216 | // after first error, rest are passed to err_cb
2217 | if (err) {
2218 | bail = true
2219 | callback(err)
2220 | // future error callbacks will go to error handler
2221 | callback = err_cb
2222 | } else if (proxy.count === 0 && !bail) {
2223 | callback(null, result)
2224 | }
2225 | }
2226 | }
2227 |
2228 | function noop() {}
2229 |
2230 | },{}],12:[function(_dereq_,module,exports){
2231 | /**
2232 | * An abstraction for slicing an arraybuffer even when
2233 | * ArrayBuffer.prototype.slice is not supported
2234 | *
2235 | * @api public
2236 | */
2237 |
2238 | module.exports = function(arraybuffer, start, end) {
2239 | var bytes = arraybuffer.byteLength;
2240 | start = start || 0;
2241 | end = end || bytes;
2242 |
2243 | if (arraybuffer.slice) { return arraybuffer.slice(start, end); }
2244 |
2245 | if (start < 0) { start += bytes; }
2246 | if (end < 0) { end += bytes; }
2247 | if (end > bytes) { end = bytes; }
2248 |
2249 | if (start >= bytes || start >= end || bytes === 0) {
2250 | return new ArrayBuffer(0);
2251 | }
2252 |
2253 | var abv = new Uint8Array(arraybuffer);
2254 | var result = new Uint8Array(end - start);
2255 | for (var i = start, ii = 0; i < end; i++, ii++) {
2256 | result[ii] = abv[i];
2257 | }
2258 | return result.buffer;
2259 | };
2260 |
2261 | },{}],13:[function(_dereq_,module,exports){
2262 | /*
2263 | * base64-arraybuffer
2264 | * https://github.com/niklasvh/base64-arraybuffer
2265 | *
2266 | * Copyright (c) 2012 Niklas von Hertzen
2267 | * Licensed under the MIT license.
2268 | */
2269 | (function(chars){
2270 | "use strict";
2271 |
2272 | exports.encode = function(arraybuffer) {
2273 | var bytes = new Uint8Array(arraybuffer),
2274 | i, len = bytes.length, base64 = "";
2275 |
2276 | for (i = 0; i < len; i+=3) {
2277 | base64 += chars[bytes[i] >> 2];
2278 | base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
2279 | base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
2280 | base64 += chars[bytes[i + 2] & 63];
2281 | }
2282 |
2283 | if ((len % 3) === 2) {
2284 | base64 = base64.substring(0, base64.length - 1) + "=";
2285 | } else if (len % 3 === 1) {
2286 | base64 = base64.substring(0, base64.length - 2) + "==";
2287 | }
2288 |
2289 | return base64;
2290 | };
2291 |
2292 | exports.decode = function(base64) {
2293 | var bufferLength = base64.length * 0.75,
2294 | len = base64.length, i, p = 0,
2295 | encoded1, encoded2, encoded3, encoded4;
2296 |
2297 | if (base64[base64.length - 1] === "=") {
2298 | bufferLength--;
2299 | if (base64[base64.length - 2] === "=") {
2300 | bufferLength--;
2301 | }
2302 | }
2303 |
2304 | var arraybuffer = new ArrayBuffer(bufferLength),
2305 | bytes = new Uint8Array(arraybuffer);
2306 |
2307 | for (i = 0; i < len; i+=4) {
2308 | encoded1 = chars.indexOf(base64[i]);
2309 | encoded2 = chars.indexOf(base64[i+1]);
2310 | encoded3 = chars.indexOf(base64[i+2]);
2311 | encoded4 = chars.indexOf(base64[i+3]);
2312 |
2313 | bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
2314 | bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
2315 | bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
2316 | }
2317 |
2318 | return arraybuffer;
2319 | };
2320 | })("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
2321 |
2322 | },{}],14:[function(_dereq_,module,exports){
2323 | (function (global){
2324 | /**
2325 | * Create a blob builder even when vendor prefixes exist
2326 | */
2327 |
2328 | var BlobBuilder = global.BlobBuilder
2329 | || global.WebKitBlobBuilder
2330 | || global.MSBlobBuilder
2331 | || global.MozBlobBuilder;
2332 |
2333 | /**
2334 | * Check if Blob constructor is supported
2335 | */
2336 |
2337 | var blobSupported = (function() {
2338 | try {
2339 | var a = new Blob(['hi']);
2340 | return a.size === 2;
2341 | } catch(e) {
2342 | return false;
2343 | }
2344 | })();
2345 |
2346 | /**
2347 | * Check if Blob constructor supports ArrayBufferViews
2348 | * Fails in Safari 6, so we need to map to ArrayBuffers there.
2349 | */
2350 |
2351 | var blobSupportsArrayBufferView = blobSupported && (function() {
2352 | try {
2353 | var b = new Blob([new Uint8Array([1,2])]);
2354 | return b.size === 2;
2355 | } catch(e) {
2356 | return false;
2357 | }
2358 | })();
2359 |
2360 | /**
2361 | * Check if BlobBuilder is supported
2362 | */
2363 |
2364 | var blobBuilderSupported = BlobBuilder
2365 | && BlobBuilder.prototype.append
2366 | && BlobBuilder.prototype.getBlob;
2367 |
2368 | /**
2369 | * Helper function that maps ArrayBufferViews to ArrayBuffers
2370 | * Used by BlobBuilder constructor and old browsers that didn't
2371 | * support it in the Blob constructor.
2372 | */
2373 |
2374 | function mapArrayBufferViews(ary) {
2375 | for (var i = 0; i < ary.length; i++) {
2376 | var chunk = ary[i];
2377 | if (chunk.buffer instanceof ArrayBuffer) {
2378 | var buf = chunk.buffer;
2379 |
2380 | // if this is a subarray, make a copy so we only
2381 | // include the subarray region from the underlying buffer
2382 | if (chunk.byteLength !== buf.byteLength) {
2383 | var copy = new Uint8Array(chunk.byteLength);
2384 | copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength));
2385 | buf = copy.buffer;
2386 | }
2387 |
2388 | ary[i] = buf;
2389 | }
2390 | }
2391 | }
2392 |
2393 | function BlobBuilderConstructor(ary, options) {
2394 | options = options || {};
2395 |
2396 | var bb = new BlobBuilder();
2397 | mapArrayBufferViews(ary);
2398 |
2399 | for (var i = 0; i < ary.length; i++) {
2400 | bb.append(ary[i]);
2401 | }
2402 |
2403 | return (options.type) ? bb.getBlob(options.type) : bb.getBlob();
2404 | };
2405 |
2406 | function BlobConstructor(ary, options) {
2407 | mapArrayBufferViews(ary);
2408 | return new Blob(ary, options || {});
2409 | };
2410 |
2411 | module.exports = (function() {
2412 | if (blobSupported) {
2413 | return blobSupportsArrayBufferView ? global.Blob : BlobConstructor;
2414 | } else if (blobBuilderSupported) {
2415 | return BlobBuilderConstructor;
2416 | } else {
2417 | return undefined;
2418 | }
2419 | })();
2420 |
2421 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {})
2422 | },{}],15:[function(_dereq_,module,exports){
2423 |
2424 | },{}],16:[function(_dereq_,module,exports){
2425 |
2426 | /**
2427 | * Expose `Emitter`.
2428 | */
2429 |
2430 | module.exports = Emitter;
2431 |
2432 | /**
2433 | * Initialize a new `Emitter`.
2434 | *
2435 | * @api public
2436 | */
2437 |
2438 | function Emitter(obj) {
2439 | if (obj) return mixin(obj);
2440 | };
2441 |
2442 | /**
2443 | * Mixin the emitter properties.
2444 | *
2445 | * @param {Object} obj
2446 | * @return {Object}
2447 | * @api private
2448 | */
2449 |
2450 | function mixin(obj) {
2451 | for (var key in Emitter.prototype) {
2452 | obj[key] = Emitter.prototype[key];
2453 | }
2454 | return obj;
2455 | }
2456 |
2457 | /**
2458 | * Listen on the given `event` with `fn`.
2459 | *
2460 | * @param {String} event
2461 | * @param {Function} fn
2462 | * @return {Emitter}
2463 | * @api public
2464 | */
2465 |
2466 | Emitter.prototype.on =
2467 | Emitter.prototype.addEventListener = function(event, fn){
2468 | this._callbacks = this._callbacks || {};
2469 | (this._callbacks[event] = this._callbacks[event] || [])
2470 | .push(fn);
2471 | return this;
2472 | };
2473 |
2474 | /**
2475 | * Adds an `event` listener that will be invoked a single
2476 | * time then automatically removed.
2477 | *
2478 | * @param {String} event
2479 | * @param {Function} fn
2480 | * @return {Emitter}
2481 | * @api public
2482 | */
2483 |
2484 | Emitter.prototype.once = function(event, fn){
2485 | var self = this;
2486 | this._callbacks = this._callbacks || {};
2487 |
2488 | function on() {
2489 | self.off(event, on);
2490 | fn.apply(this, arguments);
2491 | }
2492 |
2493 | on.fn = fn;
2494 | this.on(event, on);
2495 | return this;
2496 | };
2497 |
2498 | /**
2499 | * Remove the given callback for `event` or all
2500 | * registered callbacks.
2501 | *
2502 | * @param {String} event
2503 | * @param {Function} fn
2504 | * @return {Emitter}
2505 | * @api public
2506 | */
2507 |
2508 | Emitter.prototype.off =
2509 | Emitter.prototype.removeListener =
2510 | Emitter.prototype.removeAllListeners =
2511 | Emitter.prototype.removeEventListener = function(event, fn){
2512 | this._callbacks = this._callbacks || {};
2513 |
2514 | // all
2515 | if (0 == arguments.length) {
2516 | this._callbacks = {};
2517 | return this;
2518 | }
2519 |
2520 | // specific event
2521 | var callbacks = this._callbacks[event];
2522 | if (!callbacks) return this;
2523 |
2524 | // remove all handlers
2525 | if (1 == arguments.length) {
2526 | delete this._callbacks[event];
2527 | return this;
2528 | }
2529 |
2530 | // remove specific handler
2531 | var cb;
2532 | for (var i = 0; i < callbacks.length; i++) {
2533 | cb = callbacks[i];
2534 | if (cb === fn || cb.fn === fn) {
2535 | callbacks.splice(i, 1);
2536 | break;
2537 | }
2538 | }
2539 | return this;
2540 | };
2541 |
2542 | /**
2543 | * Emit `event` with the given args.
2544 | *
2545 | * @param {String} event
2546 | * @param {Mixed} ...
2547 | * @return {Emitter}
2548 | */
2549 |
2550 | Emitter.prototype.emit = function(event){
2551 | this._callbacks = this._callbacks || {};
2552 | var args = [].slice.call(arguments, 1)
2553 | , callbacks = this._callbacks[event];
2554 |
2555 | if (callbacks) {
2556 | callbacks = callbacks.slice(0);
2557 | for (var i = 0, len = callbacks.length; i < len; ++i) {
2558 | callbacks[i].apply(this, args);
2559 | }
2560 | }
2561 |
2562 | return this;
2563 | };
2564 |
2565 | /**
2566 | * Return array of callbacks for `event`.
2567 | *
2568 | * @param {String} event
2569 | * @return {Array}
2570 | * @api public
2571 | */
2572 |
2573 | Emitter.prototype.listeners = function(event){
2574 | this._callbacks = this._callbacks || {};
2575 | return this._callbacks[event] || [];
2576 | };
2577 |
2578 | /**
2579 | * Check if this emitter has `event` handlers.
2580 | *
2581 | * @param {String} event
2582 | * @return {Boolean}
2583 | * @api public
2584 | */
2585 |
2586 | Emitter.prototype.hasListeners = function(event){
2587 | return !! this.listeners(event).length;
2588 | };
2589 |
2590 | },{}],17:[function(_dereq_,module,exports){
2591 |
2592 | module.exports = function(a, b){
2593 | var fn = function(){};
2594 | fn.prototype = b.prototype;
2595 | a.prototype = new fn;
2596 | a.prototype.constructor = a;
2597 | };
2598 | },{}],18:[function(_dereq_,module,exports){
2599 |
2600 | /**
2601 | * This is the web browser implementation of `debug()`.
2602 | *
2603 | * Expose `debug()` as the module.
2604 | */
2605 |
2606 | exports = module.exports = _dereq_('./debug');
2607 | exports.log = log;
2608 | exports.formatArgs = formatArgs;
2609 | exports.save = save;
2610 | exports.load = load;
2611 | exports.useColors = useColors;
2612 | exports.storage = 'undefined' != typeof chrome
2613 | && 'undefined' != typeof chrome.storage
2614 | ? chrome.storage.local
2615 | : localstorage();
2616 |
2617 | /**
2618 | * Colors.
2619 | */
2620 |
2621 | exports.colors = [
2622 | 'lightseagreen',
2623 | 'forestgreen',
2624 | 'goldenrod',
2625 | 'dodgerblue',
2626 | 'darkorchid',
2627 | 'crimson'
2628 | ];
2629 |
2630 | /**
2631 | * Currently only WebKit-based Web Inspectors, Firefox >= v31,
2632 | * and the Firebug extension (any Firefox version) are known
2633 | * to support "%c" CSS customizations.
2634 | *
2635 | * TODO: add a `localStorage` variable to explicitly enable/disable colors
2636 | */
2637 |
2638 | function useColors() {
2639 | // is webkit? http://stackoverflow.com/a/16459606/376773
2640 | return ('WebkitAppearance' in document.documentElement.style) ||
2641 | // is firebug? http://stackoverflow.com/a/398120/376773
2642 | (window.console && (console.firebug || (console.exception && console.table))) ||
2643 | // is firefox >= v31?
2644 | // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
2645 | (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31);
2646 | }
2647 |
2648 | /**
2649 | * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
2650 | */
2651 |
2652 | exports.formatters.j = function(v) {
2653 | return JSON.stringify(v);
2654 | };
2655 |
2656 |
2657 | /**
2658 | * Colorize log arguments if enabled.
2659 | *
2660 | * @api public
2661 | */
2662 |
2663 | function formatArgs() {
2664 | var args = arguments;
2665 | var useColors = this.useColors;
2666 |
2667 | args[0] = (useColors ? '%c' : '')
2668 | + this.namespace
2669 | + (useColors ? ' %c' : ' ')
2670 | + args[0]
2671 | + (useColors ? '%c ' : ' ')
2672 | + '+' + exports.humanize(this.diff);
2673 |
2674 | if (!useColors) return args;
2675 |
2676 | var c = 'color: ' + this.color;
2677 | args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1));
2678 |
2679 | // the final "%c" is somewhat tricky, because there could be other
2680 | // arguments passed either before or after the %c, so we need to
2681 | // figure out the correct index to insert the CSS into
2682 | var index = 0;
2683 | var lastC = 0;
2684 | args[0].replace(/%[a-z%]/g, function(match) {
2685 | if ('%%' === match) return;
2686 | index++;
2687 | if ('%c' === match) {
2688 | // we only are interested in the *last* %c
2689 | // (the user may have provided their own)
2690 | lastC = index;
2691 | }
2692 | });
2693 |
2694 | args.splice(lastC, 0, c);
2695 | return args;
2696 | }
2697 |
2698 | /**
2699 | * Invokes `console.log()` when available.
2700 | * No-op when `console.log` is not a "function".
2701 | *
2702 | * @api public
2703 | */
2704 |
2705 | function log() {
2706 | // this hackery is required for IE8/9, where
2707 | // the `console.log` function doesn't have 'apply'
2708 | return 'object' === typeof console
2709 | && console.log
2710 | && Function.prototype.apply.call(console.log, console, arguments);
2711 | }
2712 |
2713 | /**
2714 | * Save `namespaces`.
2715 | *
2716 | * @param {String} namespaces
2717 | * @api private
2718 | */
2719 |
2720 | function save(namespaces) {
2721 | try {
2722 | if (null == namespaces) {
2723 | exports.storage.removeItem('debug');
2724 | } else {
2725 | exports.storage.debug = namespaces;
2726 | }
2727 | } catch(e) {}
2728 | }
2729 |
2730 | /**
2731 | * Load `namespaces`.
2732 | *
2733 | * @return {String} returns the previously persisted debug modes
2734 | * @api private
2735 | */
2736 |
2737 | function load() {
2738 | var r;
2739 | try {
2740 | r = exports.storage.debug;
2741 | } catch(e) {}
2742 | return r;
2743 | }
2744 |
2745 | /**
2746 | * Enable namespaces listed in `localStorage.debug` initially.
2747 | */
2748 |
2749 | exports.enable(load());
2750 |
2751 | /**
2752 | * Localstorage attempts to return the localstorage.
2753 | *
2754 | * This is necessary because safari throws
2755 | * when a user disables cookies/localstorage
2756 | * and you attempt to access it.
2757 | *
2758 | * @return {LocalStorage}
2759 | * @api private
2760 | */
2761 |
2762 | function localstorage(){
2763 | try {
2764 | return window.localStorage;
2765 | } catch (e) {}
2766 | }
2767 |
2768 | },{"./debug":19}],19:[function(_dereq_,module,exports){
2769 |
2770 | /**
2771 | * This is the common logic for both the Node.js and web browser
2772 | * implementations of `debug()`.
2773 | *
2774 | * Expose `debug()` as the module.
2775 | */
2776 |
2777 | exports = module.exports = debug;
2778 | exports.coerce = coerce;
2779 | exports.disable = disable;
2780 | exports.enable = enable;
2781 | exports.enabled = enabled;
2782 | exports.humanize = _dereq_('ms');
2783 |
2784 | /**
2785 | * The currently active debug mode names, and names to skip.
2786 | */
2787 |
2788 | exports.names = [];
2789 | exports.skips = [];
2790 |
2791 | /**
2792 | * Map of special "%n" handling functions, for the debug "format" argument.
2793 | *
2794 | * Valid key names are a single, lowercased letter, i.e. "n".
2795 | */
2796 |
2797 | exports.formatters = {};
2798 |
2799 | /**
2800 | * Previously assigned color.
2801 | */
2802 |
2803 | var prevColor = 0;
2804 |
2805 | /**
2806 | * Previous log timestamp.
2807 | */
2808 |
2809 | var prevTime;
2810 |
2811 | /**
2812 | * Select a color.
2813 | *
2814 | * @return {Number}
2815 | * @api private
2816 | */
2817 |
2818 | function selectColor() {
2819 | return exports.colors[prevColor++ % exports.colors.length];
2820 | }
2821 |
2822 | /**
2823 | * Create a debugger with the given `namespace`.
2824 | *
2825 | * @param {String} namespace
2826 | * @return {Function}
2827 | * @api public
2828 | */
2829 |
2830 | function debug(namespace) {
2831 |
2832 | // define the `disabled` version
2833 | function disabled() {
2834 | }
2835 | disabled.enabled = false;
2836 |
2837 | // define the `enabled` version
2838 | function enabled() {
2839 |
2840 | var self = enabled;
2841 |
2842 | // set `diff` timestamp
2843 | var curr = +new Date();
2844 | var ms = curr - (prevTime || curr);
2845 | self.diff = ms;
2846 | self.prev = prevTime;
2847 | self.curr = curr;
2848 | prevTime = curr;
2849 |
2850 | // add the `color` if not set
2851 | if (null == self.useColors) self.useColors = exports.useColors();
2852 | if (null == self.color && self.useColors) self.color = selectColor();
2853 |
2854 | var args = Array.prototype.slice.call(arguments);
2855 |
2856 | args[0] = exports.coerce(args[0]);
2857 |
2858 | if ('string' !== typeof args[0]) {
2859 | // anything else let's inspect with %o
2860 | args = ['%o'].concat(args);
2861 | }
2862 |
2863 | // apply any `formatters` transformations
2864 | var index = 0;
2865 | args[0] = args[0].replace(/%([a-z%])/g, function(match, format) {
2866 | // if we encounter an escaped % then don't increase the array index
2867 | if (match === '%%') return match;
2868 | index++;
2869 | var formatter = exports.formatters[format];
2870 | if ('function' === typeof formatter) {
2871 | var val = args[index];
2872 | match = formatter.call(self, val);
2873 |
2874 | // now we need to remove `args[index]` since it's inlined in the `format`
2875 | args.splice(index, 1);
2876 | index--;
2877 | }
2878 | return match;
2879 | });
2880 |
2881 | if ('function' === typeof exports.formatArgs) {
2882 | args = exports.formatArgs.apply(self, args);
2883 | }
2884 | var logFn = enabled.log || exports.log || console.log.bind(console);
2885 | logFn.apply(self, args);
2886 | }
2887 | enabled.enabled = true;
2888 |
2889 | var fn = exports.enabled(namespace) ? enabled : disabled;
2890 |
2891 | fn.namespace = namespace;
2892 |
2893 | return fn;
2894 | }
2895 |
2896 | /**
2897 | * Enables a debug mode by namespaces. This can include modes
2898 | * separated by a colon and wildcards.
2899 | *
2900 | * @param {String} namespaces
2901 | * @api public
2902 | */
2903 |
2904 | function enable(namespaces) {
2905 | exports.save(namespaces);
2906 |
2907 | var split = (namespaces || '').split(/[\s,]+/);
2908 | var len = split.length;
2909 |
2910 | for (var i = 0; i < len; i++) {
2911 | if (!split[i]) continue; // ignore empty strings
2912 | namespaces = split[i].replace(/\*/g, '.*?');
2913 | if (namespaces[0] === '-') {
2914 | exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
2915 | } else {
2916 | exports.names.push(new RegExp('^' + namespaces + '$'));
2917 | }
2918 | }
2919 | }
2920 |
2921 | /**
2922 | * Disable debug output.
2923 | *
2924 | * @api public
2925 | */
2926 |
2927 | function disable() {
2928 | exports.enable('');
2929 | }
2930 |
2931 | /**
2932 | * Returns true if the given mode name is enabled, false otherwise.
2933 | *
2934 | * @param {String} name
2935 | * @return {Boolean}
2936 | * @api public
2937 | */
2938 |
2939 | function enabled(name) {
2940 | var i, len;
2941 | for (i = 0, len = exports.skips.length; i < len; i++) {
2942 | if (exports.skips[i].test(name)) {
2943 | return false;
2944 | }
2945 | }
2946 | for (i = 0, len = exports.names.length; i < len; i++) {
2947 | if (exports.names[i].test(name)) {
2948 | return true;
2949 | }
2950 | }
2951 | return false;
2952 | }
2953 |
2954 | /**
2955 | * Coerce `val`.
2956 | *
2957 | * @param {Mixed} val
2958 | * @return {Mixed}
2959 | * @api private
2960 | */
2961 |
2962 | function coerce(val) {
2963 | if (val instanceof Error) return val.stack || val.message;
2964 | return val;
2965 | }
2966 |
2967 | },{"ms":26}],20:[function(_dereq_,module,exports){
2968 | (function (global){
2969 | /**
2970 | * Module dependencies.
2971 | */
2972 |
2973 | var keys = _dereq_('./keys');
2974 | var hasBinary = _dereq_('has-binary');
2975 | var sliceBuffer = _dereq_('arraybuffer.slice');
2976 | var base64encoder = _dereq_('base64-arraybuffer');
2977 | var after = _dereq_('after');
2978 | var utf8 = _dereq_('utf8');
2979 |
2980 | /**
2981 | * Check if we are running an android browser. That requires us to use
2982 | * ArrayBuffer with polling transports...
2983 | *
2984 | * http://ghinda.net/jpeg-blob-ajax-android/
2985 | */
2986 |
2987 | var isAndroid = navigator.userAgent.match(/Android/i);
2988 |
2989 | /**
2990 | * Check if we are running in PhantomJS.
2991 | * Uploading a Blob with PhantomJS does not work correctly, as reported here:
2992 | * https://github.com/ariya/phantomjs/issues/11395
2993 | * @type boolean
2994 | */
2995 | var isPhantomJS = /PhantomJS/i.test(navigator.userAgent);
2996 |
2997 | /**
2998 | * When true, avoids using Blobs to encode payloads.
2999 | * @type boolean
3000 | */
3001 | var dontSendBlobs = isAndroid || isPhantomJS;
3002 |
3003 | /**
3004 | * Current protocol version.
3005 | */
3006 |
3007 | exports.protocol = 3;
3008 |
3009 | /**
3010 | * Packet types.
3011 | */
3012 |
3013 | var packets = exports.packets = {
3014 | open: 0 // non-ws
3015 | , close: 1 // non-ws
3016 | , ping: 2
3017 | , pong: 3
3018 | , message: 4
3019 | , upgrade: 5
3020 | , noop: 6
3021 | };
3022 |
3023 | var packetslist = keys(packets);
3024 |
3025 | /**
3026 | * Premade error packet.
3027 | */
3028 |
3029 | var err = { type: 'error', data: 'parser error' };
3030 |
3031 | /**
3032 | * Create a blob api even for blob builder when vendor prefixes exist
3033 | */
3034 |
3035 | var Blob = _dereq_('blob');
3036 |
3037 | /**
3038 | * Encodes a packet.
3039 | *
3040 | * [ ]
3041 | *
3042 | * Example:
3043 | *
3044 | * 5hello world
3045 | * 3
3046 | * 4
3047 | *
3048 | * Binary is encoded in an identical principle
3049 | *
3050 | * @api private
3051 | */
3052 |
3053 | exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
3054 | if ('function' == typeof supportsBinary) {
3055 | callback = supportsBinary;
3056 | supportsBinary = false;
3057 | }
3058 |
3059 | if ('function' == typeof utf8encode) {
3060 | callback = utf8encode;
3061 | utf8encode = null;
3062 | }
3063 |
3064 | var data = (packet.data === undefined)
3065 | ? undefined
3066 | : packet.data.buffer || packet.data;
3067 |
3068 | if (global.ArrayBuffer && data instanceof ArrayBuffer) {
3069 | return encodeArrayBuffer(packet, supportsBinary, callback);
3070 | } else if (Blob && data instanceof global.Blob) {
3071 | return encodeBlob(packet, supportsBinary, callback);
3072 | }
3073 |
3074 | // might be an object with { base64: true, data: dataAsBase64String }
3075 | if (data && data.base64) {
3076 | return encodeBase64Object(packet, callback);
3077 | }
3078 |
3079 | // Sending data as a utf-8 string
3080 | var encoded = packets[packet.type];
3081 |
3082 | // data fragment is optional
3083 | if (undefined !== packet.data) {
3084 | encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
3085 | }
3086 |
3087 | return callback('' + encoded);
3088 |
3089 | };
3090 |
3091 | function encodeBase64Object(packet, callback) {
3092 | // packet data is an object { base64: true, data: dataAsBase64String }
3093 | var message = 'b' + exports.packets[packet.type] + packet.data.data;
3094 | return callback(message);
3095 | }
3096 |
3097 | /**
3098 | * Encode packet helpers for binary types
3099 | */
3100 |
3101 | function encodeArrayBuffer(packet, supportsBinary, callback) {
3102 | if (!supportsBinary) {
3103 | return exports.encodeBase64Packet(packet, callback);
3104 | }
3105 |
3106 | var data = packet.data;
3107 | var contentArray = new Uint8Array(data);
3108 | var resultBuffer = new Uint8Array(1 + data.byteLength);
3109 |
3110 | resultBuffer[0] = packets[packet.type];
3111 | for (var i = 0; i < contentArray.length; i++) {
3112 | resultBuffer[i+1] = contentArray[i];
3113 | }
3114 |
3115 | return callback(resultBuffer.buffer);
3116 | }
3117 |
3118 | function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) {
3119 | if (!supportsBinary) {
3120 | return exports.encodeBase64Packet(packet, callback);
3121 | }
3122 |
3123 | var fr = new FileReader();
3124 | fr.onload = function() {
3125 | packet.data = fr.result;
3126 | exports.encodePacket(packet, supportsBinary, true, callback);
3127 | };
3128 | return fr.readAsArrayBuffer(packet.data);
3129 | }
3130 |
3131 | function encodeBlob(packet, supportsBinary, callback) {
3132 | if (!supportsBinary) {
3133 | return exports.encodeBase64Packet(packet, callback);
3134 | }
3135 |
3136 | if (dontSendBlobs) {
3137 | return encodeBlobAsArrayBuffer(packet, supportsBinary, callback);
3138 | }
3139 |
3140 | var length = new Uint8Array(1);
3141 | length[0] = packets[packet.type];
3142 | var blob = new Blob([length.buffer, packet.data]);
3143 |
3144 | return callback(blob);
3145 | }
3146 |
3147 | /**
3148 | * Encodes a packet with binary data in a base64 string
3149 | *
3150 | * @param {Object} packet, has `type` and `data`
3151 | * @return {String} base64 encoded message
3152 | */
3153 |
3154 | exports.encodeBase64Packet = function(packet, callback) {
3155 | var message = 'b' + exports.packets[packet.type];
3156 | if (Blob && packet.data instanceof global.Blob) {
3157 | var fr = new FileReader();
3158 | fr.onload = function() {
3159 | var b64 = fr.result.split(',')[1];
3160 | callback(message + b64);
3161 | };
3162 | return fr.readAsDataURL(packet.data);
3163 | }
3164 |
3165 | var b64data;
3166 | try {
3167 | b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data));
3168 | } catch (e) {
3169 | // iPhone Safari doesn't let you apply with typed arrays
3170 | var typed = new Uint8Array(packet.data);
3171 | var basic = new Array(typed.length);
3172 | for (var i = 0; i < typed.length; i++) {
3173 | basic[i] = typed[i];
3174 | }
3175 | b64data = String.fromCharCode.apply(null, basic);
3176 | }
3177 | message += global.btoa(b64data);
3178 | return callback(message);
3179 | };
3180 |
3181 | /**
3182 | * Decodes a packet. Changes format to Blob if requested.
3183 | *
3184 | * @return {Object} with `type` and `data` (if any)
3185 | * @api private
3186 | */
3187 |
3188 | exports.decodePacket = function (data, binaryType, utf8decode) {
3189 | // String data
3190 | if (typeof data == 'string' || data === undefined) {
3191 | if (data.charAt(0) == 'b') {
3192 | return exports.decodeBase64Packet(data.substr(1), binaryType);
3193 | }
3194 |
3195 | if (utf8decode) {
3196 | try {
3197 | data = utf8.decode(data);
3198 | } catch (e) {
3199 | return err;
3200 | }
3201 | }
3202 | var type = data.charAt(0);
3203 |
3204 | if (Number(type) != type || !packetslist[type]) {
3205 | return err;
3206 | }
3207 |
3208 | if (data.length > 1) {
3209 | return { type: packetslist[type], data: data.substring(1) };
3210 | } else {
3211 | return { type: packetslist[type] };
3212 | }
3213 | }
3214 |
3215 | var asArray = new Uint8Array(data);
3216 | var type = asArray[0];
3217 | var rest = sliceBuffer(data, 1);
3218 | if (Blob && binaryType === 'blob') {
3219 | rest = new Blob([rest]);
3220 | }
3221 | return { type: packetslist[type], data: rest };
3222 | };
3223 |
3224 | /**
3225 | * Decodes a packet encoded in a base64 string
3226 | *
3227 | * @param {String} base64 encoded message
3228 | * @return {Object} with `type` and `data` (if any)
3229 | */
3230 |
3231 | exports.decodeBase64Packet = function(msg, binaryType) {
3232 | var type = packetslist[msg.charAt(0)];
3233 | if (!global.ArrayBuffer) {
3234 | return { type: type, data: { base64: true, data: msg.substr(1) } };
3235 | }
3236 |
3237 | var data = base64encoder.decode(msg.substr(1));
3238 |
3239 | if (binaryType === 'blob' && Blob) {
3240 | data = new Blob([data]);
3241 | }
3242 |
3243 | return { type: type, data: data };
3244 | };
3245 |
3246 | /**
3247 | * Encodes multiple messages (payload).
3248 | *
3249 | * :data
3250 | *
3251 | * Example:
3252 | *
3253 | * 11:hello world2:hi
3254 | *
3255 | * If any contents are binary, they will be encoded as base64 strings. Base64
3256 | * encoded strings are marked with a b before the length specifier
3257 | *
3258 | * @param {Array} packets
3259 | * @api private
3260 | */
3261 |
3262 | exports.encodePayload = function (packets, supportsBinary, callback) {
3263 | if (typeof supportsBinary == 'function') {
3264 | callback = supportsBinary;
3265 | supportsBinary = null;
3266 | }
3267 |
3268 | var isBinary = hasBinary(packets);
3269 |
3270 | if (supportsBinary && isBinary) {
3271 | if (Blob && !dontSendBlobs) {
3272 | return exports.encodePayloadAsBlob(packets, callback);
3273 | }
3274 |
3275 | return exports.encodePayloadAsArrayBuffer(packets, callback);
3276 | }
3277 |
3278 | if (!packets.length) {
3279 | return callback('0:');
3280 | }
3281 |
3282 | function setLengthHeader(message) {
3283 | return message.length + ':' + message;
3284 | }
3285 |
3286 | function encodeOne(packet, doneCallback) {
3287 | exports.encodePacket(packet, !isBinary ? false : supportsBinary, true, function(message) {
3288 | doneCallback(null, setLengthHeader(message));
3289 | });
3290 | }
3291 |
3292 | map(packets, encodeOne, function(err, results) {
3293 | return callback(results.join(''));
3294 | });
3295 | };
3296 |
3297 | /**
3298 | * Async array map using after
3299 | */
3300 |
3301 | function map(ary, each, done) {
3302 | var result = new Array(ary.length);
3303 | var next = after(ary.length, done);
3304 |
3305 | var eachWithIndex = function(i, el, cb) {
3306 | each(el, function(error, msg) {
3307 | result[i] = msg;
3308 | cb(error, result);
3309 | });
3310 | };
3311 |
3312 | for (var i = 0; i < ary.length; i++) {
3313 | eachWithIndex(i, ary[i], next);
3314 | }
3315 | }
3316 |
3317 | /*
3318 | * Decodes data when a payload is maybe expected. Possible binary contents are
3319 | * decoded from their base64 representation
3320 | *
3321 | * @param {String} data, callback method
3322 | * @api public
3323 | */
3324 |
3325 | exports.decodePayload = function (data, binaryType, callback) {
3326 | if (typeof data != 'string') {
3327 | return exports.decodePayloadAsBinary(data, binaryType, callback);
3328 | }
3329 |
3330 | if (typeof binaryType === 'function') {
3331 | callback = binaryType;
3332 | binaryType = null;
3333 | }
3334 |
3335 | var packet;
3336 | if (data == '') {
3337 | // parser error - ignoring payload
3338 | return callback(err, 0, 1);
3339 | }
3340 |
3341 | var length = ''
3342 | , n, msg;
3343 |
3344 | for (var i = 0, l = data.length; i < l; i++) {
3345 | var chr = data.charAt(i);
3346 |
3347 | if (':' != chr) {
3348 | length += chr;
3349 | } else {
3350 | if ('' == length || (length != (n = Number(length)))) {
3351 | // parser error - ignoring payload
3352 | return callback(err, 0, 1);
3353 | }
3354 |
3355 | msg = data.substr(i + 1, n);
3356 |
3357 | if (length != msg.length) {
3358 | // parser error - ignoring payload
3359 | return callback(err, 0, 1);
3360 | }
3361 |
3362 | if (msg.length) {
3363 | packet = exports.decodePacket(msg, binaryType, true);
3364 |
3365 | if (err.type == packet.type && err.data == packet.data) {
3366 | // parser error in individual packet - ignoring payload
3367 | return callback(err, 0, 1);
3368 | }
3369 |
3370 | var ret = callback(packet, i + n, l);
3371 | if (false === ret) return;
3372 | }
3373 |
3374 | // advance cursor
3375 | i += n;
3376 | length = '';
3377 | }
3378 | }
3379 |
3380 | if (length != '') {
3381 | // parser error - ignoring payload
3382 | return callback(err, 0, 1);
3383 | }
3384 |
3385 | };
3386 |
3387 | /**
3388 | * Encodes multiple messages (payload) as binary.
3389 | *
3390 | * <1 = binary, 0 = string>[...]
3392 | *
3393 | * Example:
3394 | * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
3395 | *
3396 | * @param {Array} packets
3397 | * @return {ArrayBuffer} encoded payload
3398 | * @api private
3399 | */
3400 |
3401 | exports.encodePayloadAsArrayBuffer = function(packets, callback) {
3402 | if (!packets.length) {
3403 | return callback(new ArrayBuffer(0));
3404 | }
3405 |
3406 | function encodeOne(packet, doneCallback) {
3407 | exports.encodePacket(packet, true, true, function(data) {
3408 | return doneCallback(null, data);
3409 | });
3410 | }
3411 |
3412 | map(packets, encodeOne, function(err, encodedPackets) {
3413 | var totalLength = encodedPackets.reduce(function(acc, p) {
3414 | var len;
3415 | if (typeof p === 'string'){
3416 | len = p.length;
3417 | } else {
3418 | len = p.byteLength;
3419 | }
3420 | return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2
3421 | }, 0);
3422 |
3423 | var resultArray = new Uint8Array(totalLength);
3424 |
3425 | var bufferIndex = 0;
3426 | encodedPackets.forEach(function(p) {
3427 | var isString = typeof p === 'string';
3428 | var ab = p;
3429 | if (isString) {
3430 | var view = new Uint8Array(p.length);
3431 | for (var i = 0; i < p.length; i++) {
3432 | view[i] = p.charCodeAt(i);
3433 | }
3434 | ab = view.buffer;
3435 | }
3436 |
3437 | if (isString) { // not true binary
3438 | resultArray[bufferIndex++] = 0;
3439 | } else { // true binary
3440 | resultArray[bufferIndex++] = 1;
3441 | }
3442 |
3443 | var lenStr = ab.byteLength.toString();
3444 | for (var i = 0; i < lenStr.length; i++) {
3445 | resultArray[bufferIndex++] = parseInt(lenStr[i]);
3446 | }
3447 | resultArray[bufferIndex++] = 255;
3448 |
3449 | var view = new Uint8Array(ab);
3450 | for (var i = 0; i < view.length; i++) {
3451 | resultArray[bufferIndex++] = view[i];
3452 | }
3453 | });
3454 |
3455 | return callback(resultArray.buffer);
3456 | });
3457 | };
3458 |
3459 | /**
3460 | * Encode as Blob
3461 | */
3462 |
3463 | exports.encodePayloadAsBlob = function(packets, callback) {
3464 | function encodeOne(packet, doneCallback) {
3465 | exports.encodePacket(packet, true, true, function(encoded) {
3466 | var binaryIdentifier = new Uint8Array(1);
3467 | binaryIdentifier[0] = 1;
3468 | if (typeof encoded === 'string') {
3469 | var view = new Uint8Array(encoded.length);
3470 | for (var i = 0; i < encoded.length; i++) {
3471 | view[i] = encoded.charCodeAt(i);
3472 | }
3473 | encoded = view.buffer;
3474 | binaryIdentifier[0] = 0;
3475 | }
3476 |
3477 | var len = (encoded instanceof ArrayBuffer)
3478 | ? encoded.byteLength
3479 | : encoded.size;
3480 |
3481 | var lenStr = len.toString();
3482 | var lengthAry = new Uint8Array(lenStr.length + 1);
3483 | for (var i = 0; i < lenStr.length; i++) {
3484 | lengthAry[i] = parseInt(lenStr[i]);
3485 | }
3486 | lengthAry[lenStr.length] = 255;
3487 |
3488 | if (Blob) {
3489 | var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]);
3490 | doneCallback(null, blob);
3491 | }
3492 | });
3493 | }
3494 |
3495 | map(packets, encodeOne, function(err, results) {
3496 | return callback(new Blob(results));
3497 | });
3498 | };
3499 |
3500 | /*
3501 | * Decodes data when a payload is maybe expected. Strings are decoded by
3502 | * interpreting each byte as a key code for entries marked to start with 0. See
3503 | * description of encodePayloadAsBinary
3504 | *
3505 | * @param {ArrayBuffer} data, callback method
3506 | * @api public
3507 | */
3508 |
3509 | exports.decodePayloadAsBinary = function (data, binaryType, callback) {
3510 | if (typeof binaryType === 'function') {
3511 | callback = binaryType;
3512 | binaryType = null;
3513 | }
3514 |
3515 | var bufferTail = data;
3516 | var buffers = [];
3517 |
3518 | var numberTooLong = false;
3519 | while (bufferTail.byteLength > 0) {
3520 | var tailArray = new Uint8Array(bufferTail);
3521 | var isString = tailArray[0] === 0;
3522 | var msgLength = '';
3523 |
3524 | for (var i = 1; ; i++) {
3525 | if (tailArray[i] == 255) break;
3526 |
3527 | if (msgLength.length > 310) {
3528 | numberTooLong = true;
3529 | break;
3530 | }
3531 |
3532 | msgLength += tailArray[i];
3533 | }
3534 |
3535 | if(numberTooLong) return callback(err, 0, 1);
3536 |
3537 | bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length);
3538 | msgLength = parseInt(msgLength);
3539 |
3540 | var msg = sliceBuffer(bufferTail, 0, msgLength);
3541 | if (isString) {
3542 | try {
3543 | msg = String.fromCharCode.apply(null, new Uint8Array(msg));
3544 | } catch (e) {
3545 | // iPhone Safari doesn't let you apply to typed arrays
3546 | var typed = new Uint8Array(msg);
3547 | msg = '';
3548 | for (var i = 0; i < typed.length; i++) {
3549 | msg += String.fromCharCode(typed[i]);
3550 | }
3551 | }
3552 | }
3553 |
3554 | buffers.push(msg);
3555 | bufferTail = sliceBuffer(bufferTail, msgLength);
3556 | }
3557 |
3558 | var total = buffers.length;
3559 | buffers.forEach(function(buffer, i) {
3560 | callback(exports.decodePacket(buffer, binaryType, true), i, total);
3561 | });
3562 | };
3563 |
3564 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {})
3565 | },{"./keys":21,"after":11,"arraybuffer.slice":12,"base64-arraybuffer":13,"blob":14,"has-binary":22,"utf8":30}],21:[function(_dereq_,module,exports){
3566 |
3567 | /**
3568 | * Gets the keys for an object.
3569 | *
3570 | * @return {Array} keys
3571 | * @api private
3572 | */
3573 |
3574 | module.exports = Object.keys || function keys (obj){
3575 | var arr = [];
3576 | var has = Object.prototype.hasOwnProperty;
3577 |
3578 | for (var i in obj) {
3579 | if (has.call(obj, i)) {
3580 | arr.push(i);
3581 | }
3582 | }
3583 | return arr;
3584 | };
3585 |
3586 | },{}],22:[function(_dereq_,module,exports){
3587 | (function (global){
3588 |
3589 | /*
3590 | * Module requirements.
3591 | */
3592 |
3593 | var isArray = _dereq_('isarray');
3594 |
3595 | /**
3596 | * Module exports.
3597 | */
3598 |
3599 | module.exports = hasBinary;
3600 |
3601 | /**
3602 | * Checks for binary data.
3603 | *
3604 | * Right now only Buffer and ArrayBuffer are supported..
3605 | *
3606 | * @param {Object} anything
3607 | * @api public
3608 | */
3609 |
3610 | function hasBinary(data) {
3611 |
3612 | function _hasBinary(obj) {
3613 | if (!obj) return false;
3614 |
3615 | if ( (global.Buffer && global.Buffer.isBuffer(obj)) ||
3616 | (global.ArrayBuffer && obj instanceof ArrayBuffer) ||
3617 | (global.Blob && obj instanceof Blob) ||
3618 | (global.File && obj instanceof File)
3619 | ) {
3620 | return true;
3621 | }
3622 |
3623 | if (isArray(obj)) {
3624 | for (var i = 0; i < obj.length; i++) {
3625 | if (_hasBinary(obj[i])) {
3626 | return true;
3627 | }
3628 | }
3629 | } else if (obj && 'object' == typeof obj) {
3630 | if (obj.toJSON) {
3631 | obj = obj.toJSON();
3632 | }
3633 |
3634 | for (var key in obj) {
3635 | if (Object.prototype.hasOwnProperty.call(obj, key) && _hasBinary(obj[key])) {
3636 | return true;
3637 | }
3638 | }
3639 | }
3640 |
3641 | return false;
3642 | }
3643 |
3644 | return _hasBinary(data);
3645 | }
3646 |
3647 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {})
3648 | },{"isarray":25}],23:[function(_dereq_,module,exports){
3649 |
3650 | /**
3651 | * Module exports.
3652 | *
3653 | * Logic borrowed from Modernizr:
3654 | *
3655 | * - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js
3656 | */
3657 |
3658 | try {
3659 | module.exports = typeof XMLHttpRequest !== 'undefined' &&
3660 | 'withCredentials' in new XMLHttpRequest();
3661 | } catch (err) {
3662 | // if XMLHttp support is disabled in IE then it will throw
3663 | // when trying to create
3664 | module.exports = false;
3665 | }
3666 |
3667 | },{}],24:[function(_dereq_,module,exports){
3668 |
3669 | var indexOf = [].indexOf;
3670 |
3671 | module.exports = function(arr, obj){
3672 | if (indexOf) return arr.indexOf(obj);
3673 | for (var i = 0; i < arr.length; ++i) {
3674 | if (arr[i] === obj) return i;
3675 | }
3676 | return -1;
3677 | };
3678 | },{}],25:[function(_dereq_,module,exports){
3679 | module.exports = Array.isArray || function (arr) {
3680 | return Object.prototype.toString.call(arr) == '[object Array]';
3681 | };
3682 |
3683 | },{}],26:[function(_dereq_,module,exports){
3684 | /**
3685 | * Helpers.
3686 | */
3687 |
3688 | var s = 1000;
3689 | var m = s * 60;
3690 | var h = m * 60;
3691 | var d = h * 24;
3692 | var y = d * 365.25;
3693 |
3694 | /**
3695 | * Parse or format the given `val`.
3696 | *
3697 | * Options:
3698 | *
3699 | * - `long` verbose formatting [false]
3700 | *
3701 | * @param {String|Number} val
3702 | * @param {Object} options
3703 | * @return {String|Number}
3704 | * @api public
3705 | */
3706 |
3707 | module.exports = function(val, options){
3708 | options = options || {};
3709 | if ('string' == typeof val) return parse(val);
3710 | return options.long
3711 | ? long(val)
3712 | : short(val);
3713 | };
3714 |
3715 | /**
3716 | * Parse the given `str` and return milliseconds.
3717 | *
3718 | * @param {String} str
3719 | * @return {Number}
3720 | * @api private
3721 | */
3722 |
3723 | function parse(str) {
3724 | str = '' + str;
3725 | if (str.length > 10000) return;
3726 | var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str);
3727 | if (!match) return;
3728 | var n = parseFloat(match[1]);
3729 | var type = (match[2] || 'ms').toLowerCase();
3730 | switch (type) {
3731 | case 'years':
3732 | case 'year':
3733 | case 'yrs':
3734 | case 'yr':
3735 | case 'y':
3736 | return n * y;
3737 | case 'days':
3738 | case 'day':
3739 | case 'd':
3740 | return n * d;
3741 | case 'hours':
3742 | case 'hour':
3743 | case 'hrs':
3744 | case 'hr':
3745 | case 'h':
3746 | return n * h;
3747 | case 'minutes':
3748 | case 'minute':
3749 | case 'mins':
3750 | case 'min':
3751 | case 'm':
3752 | return n * m;
3753 | case 'seconds':
3754 | case 'second':
3755 | case 'secs':
3756 | case 'sec':
3757 | case 's':
3758 | return n * s;
3759 | case 'milliseconds':
3760 | case 'millisecond':
3761 | case 'msecs':
3762 | case 'msec':
3763 | case 'ms':
3764 | return n;
3765 | }
3766 | }
3767 |
3768 | /**
3769 | * Short format for `ms`.
3770 | *
3771 | * @param {Number} ms
3772 | * @return {String}
3773 | * @api private
3774 | */
3775 |
3776 | function short(ms) {
3777 | if (ms >= d) return Math.round(ms / d) + 'd';
3778 | if (ms >= h) return Math.round(ms / h) + 'h';
3779 | if (ms >= m) return Math.round(ms / m) + 'm';
3780 | if (ms >= s) return Math.round(ms / s) + 's';
3781 | return ms + 'ms';
3782 | }
3783 |
3784 | /**
3785 | * Long format for `ms`.
3786 | *
3787 | * @param {Number} ms
3788 | * @return {String}
3789 | * @api private
3790 | */
3791 |
3792 | function long(ms) {
3793 | return plural(ms, d, 'day')
3794 | || plural(ms, h, 'hour')
3795 | || plural(ms, m, 'minute')
3796 | || plural(ms, s, 'second')
3797 | || ms + ' ms';
3798 | }
3799 |
3800 | /**
3801 | * Pluralization helper.
3802 | */
3803 |
3804 | function plural(ms, n, name) {
3805 | if (ms < n) return;
3806 | if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name;
3807 | return Math.ceil(ms / n) + ' ' + name + 's';
3808 | }
3809 |
3810 | },{}],27:[function(_dereq_,module,exports){
3811 | (function (global){
3812 | /**
3813 | * JSON parse.
3814 | *
3815 | * @see Based on jQuery#parseJSON (MIT) and JSON2
3816 | * @api private
3817 | */
3818 |
3819 | var rvalidchars = /^[\],:{}\s]*$/;
3820 | var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
3821 | var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
3822 | var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g;
3823 | var rtrimLeft = /^\s+/;
3824 | var rtrimRight = /\s+$/;
3825 |
3826 | module.exports = function parsejson(data) {
3827 | if ('string' != typeof data || !data) {
3828 | return null;
3829 | }
3830 |
3831 | data = data.replace(rtrimLeft, '').replace(rtrimRight, '');
3832 |
3833 | // Attempt to parse using the native JSON parser first
3834 | if (global.JSON && JSON.parse) {
3835 | return JSON.parse(data);
3836 | }
3837 |
3838 | if (rvalidchars.test(data.replace(rvalidescape, '@')
3839 | .replace(rvalidtokens, ']')
3840 | .replace(rvalidbraces, ''))) {
3841 | return (new Function('return ' + data))();
3842 | }
3843 | };
3844 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {})
3845 | },{}],28:[function(_dereq_,module,exports){
3846 | /**
3847 | * Compiles a querystring
3848 | * Returns string representation of the object
3849 | *
3850 | * @param {Object}
3851 | * @api private
3852 | */
3853 |
3854 | exports.encode = function (obj) {
3855 | var str = '';
3856 |
3857 | for (var i in obj) {
3858 | if (obj.hasOwnProperty(i)) {
3859 | if (str.length) str += '&';
3860 | str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]);
3861 | }
3862 | }
3863 |
3864 | return str;
3865 | };
3866 |
3867 | /**
3868 | * Parses a simple querystring into an object
3869 | *
3870 | * @param {String} qs
3871 | * @api private
3872 | */
3873 |
3874 | exports.decode = function(qs){
3875 | var qry = {};
3876 | var pairs = qs.split('&');
3877 | for (var i = 0, l = pairs.length; i < l; i++) {
3878 | var pair = pairs[i].split('=');
3879 | qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
3880 | }
3881 | return qry;
3882 | };
3883 |
3884 | },{}],29:[function(_dereq_,module,exports){
3885 | /**
3886 | * Parses an URI
3887 | *
3888 | * @author Steven Levithan (MIT license)
3889 | * @api private
3890 | */
3891 |
3892 | var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
3893 |
3894 | var parts = [
3895 | 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
3896 | ];
3897 |
3898 | module.exports = function parseuri(str) {
3899 | var src = str,
3900 | b = str.indexOf('['),
3901 | e = str.indexOf(']');
3902 |
3903 | if (b != -1 && e != -1) {
3904 | str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length);
3905 | }
3906 |
3907 | var m = re.exec(str || ''),
3908 | uri = {},
3909 | i = 14;
3910 |
3911 | while (i--) {
3912 | uri[parts[i]] = m[i] || '';
3913 | }
3914 |
3915 | if (b != -1 && e != -1) {
3916 | uri.source = src;
3917 | uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':');
3918 | uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':');
3919 | uri.ipv6uri = true;
3920 | }
3921 |
3922 | return uri;
3923 | };
3924 |
3925 | },{}],30:[function(_dereq_,module,exports){
3926 | (function (global){
3927 | /*! https://mths.be/utf8js v2.0.0 by @mathias */
3928 | ;(function(root) {
3929 |
3930 | // Detect free variables `exports`
3931 | var freeExports = typeof exports == 'object' && exports;
3932 |
3933 | // Detect free variable `module`
3934 | var freeModule = typeof module == 'object' && module &&
3935 | module.exports == freeExports && module;
3936 |
3937 | // Detect free variable `global`, from Node.js or Browserified code,
3938 | // and use it as `root`
3939 | var freeGlobal = typeof global == 'object' && global;
3940 | if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
3941 | root = freeGlobal;
3942 | }
3943 |
3944 | /*--------------------------------------------------------------------------*/
3945 |
3946 | var stringFromCharCode = String.fromCharCode;
3947 |
3948 | // Taken from https://mths.be/punycode
3949 | function ucs2decode(string) {
3950 | var output = [];
3951 | var counter = 0;
3952 | var length = string.length;
3953 | var value;
3954 | var extra;
3955 | while (counter < length) {
3956 | value = string.charCodeAt(counter++);
3957 | if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
3958 | // high surrogate, and there is a next character
3959 | extra = string.charCodeAt(counter++);
3960 | if ((extra & 0xFC00) == 0xDC00) { // low surrogate
3961 | output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
3962 | } else {
3963 | // unmatched surrogate; only append this code unit, in case the next
3964 | // code unit is the high surrogate of a surrogate pair
3965 | output.push(value);
3966 | counter--;
3967 | }
3968 | } else {
3969 | output.push(value);
3970 | }
3971 | }
3972 | return output;
3973 | }
3974 |
3975 | // Taken from https://mths.be/punycode
3976 | function ucs2encode(array) {
3977 | var length = array.length;
3978 | var index = -1;
3979 | var value;
3980 | var output = '';
3981 | while (++index < length) {
3982 | value = array[index];
3983 | if (value > 0xFFFF) {
3984 | value -= 0x10000;
3985 | output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
3986 | value = 0xDC00 | value & 0x3FF;
3987 | }
3988 | output += stringFromCharCode(value);
3989 | }
3990 | return output;
3991 | }
3992 |
3993 | function checkScalarValue(codePoint) {
3994 | if (codePoint >= 0xD800 && codePoint <= 0xDFFF) {
3995 | throw Error(
3996 | 'Lone surrogate U+' + codePoint.toString(16).toUpperCase() +
3997 | ' is not a scalar value'
3998 | );
3999 | }
4000 | }
4001 | /*--------------------------------------------------------------------------*/
4002 |
4003 | function createByte(codePoint, shift) {
4004 | return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80);
4005 | }
4006 |
4007 | function encodeCodePoint(codePoint) {
4008 | if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence
4009 | return stringFromCharCode(codePoint);
4010 | }
4011 | var symbol = '';
4012 | if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence
4013 | symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0);
4014 | }
4015 | else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence
4016 | checkScalarValue(codePoint);
4017 | symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0);
4018 | symbol += createByte(codePoint, 6);
4019 | }
4020 | else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence
4021 | symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0);
4022 | symbol += createByte(codePoint, 12);
4023 | symbol += createByte(codePoint, 6);
4024 | }
4025 | symbol += stringFromCharCode((codePoint & 0x3F) | 0x80);
4026 | return symbol;
4027 | }
4028 |
4029 | function utf8encode(string) {
4030 | var codePoints = ucs2decode(string);
4031 | var length = codePoints.length;
4032 | var index = -1;
4033 | var codePoint;
4034 | var byteString = '';
4035 | while (++index < length) {
4036 | codePoint = codePoints[index];
4037 | byteString += encodeCodePoint(codePoint);
4038 | }
4039 | return byteString;
4040 | }
4041 |
4042 | /*--------------------------------------------------------------------------*/
4043 |
4044 | function readContinuationByte() {
4045 | if (byteIndex >= byteCount) {
4046 | throw Error('Invalid byte index');
4047 | }
4048 |
4049 | var continuationByte = byteArray[byteIndex] & 0xFF;
4050 | byteIndex++;
4051 |
4052 | if ((continuationByte & 0xC0) == 0x80) {
4053 | return continuationByte & 0x3F;
4054 | }
4055 |
4056 | // If we end up here, it’s not a continuation byte
4057 | throw Error('Invalid continuation byte');
4058 | }
4059 |
4060 | function decodeSymbol() {
4061 | var byte1;
4062 | var byte2;
4063 | var byte3;
4064 | var byte4;
4065 | var codePoint;
4066 |
4067 | if (byteIndex > byteCount) {
4068 | throw Error('Invalid byte index');
4069 | }
4070 |
4071 | if (byteIndex == byteCount) {
4072 | return false;
4073 | }
4074 |
4075 | // Read first byte
4076 | byte1 = byteArray[byteIndex] & 0xFF;
4077 | byteIndex++;
4078 |
4079 | // 1-byte sequence (no continuation bytes)
4080 | if ((byte1 & 0x80) == 0) {
4081 | return byte1;
4082 | }
4083 |
4084 | // 2-byte sequence
4085 | if ((byte1 & 0xE0) == 0xC0) {
4086 | var byte2 = readContinuationByte();
4087 | codePoint = ((byte1 & 0x1F) << 6) | byte2;
4088 | if (codePoint >= 0x80) {
4089 | return codePoint;
4090 | } else {
4091 | throw Error('Invalid continuation byte');
4092 | }
4093 | }
4094 |
4095 | // 3-byte sequence (may include unpaired surrogates)
4096 | if ((byte1 & 0xF0) == 0xE0) {
4097 | byte2 = readContinuationByte();
4098 | byte3 = readContinuationByte();
4099 | codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3;
4100 | if (codePoint >= 0x0800) {
4101 | checkScalarValue(codePoint);
4102 | return codePoint;
4103 | } else {
4104 | throw Error('Invalid continuation byte');
4105 | }
4106 | }
4107 |
4108 | // 4-byte sequence
4109 | if ((byte1 & 0xF8) == 0xF0) {
4110 | byte2 = readContinuationByte();
4111 | byte3 = readContinuationByte();
4112 | byte4 = readContinuationByte();
4113 | codePoint = ((byte1 & 0x0F) << 0x12) | (byte2 << 0x0C) |
4114 | (byte3 << 0x06) | byte4;
4115 | if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) {
4116 | return codePoint;
4117 | }
4118 | }
4119 |
4120 | throw Error('Invalid UTF-8 detected');
4121 | }
4122 |
4123 | var byteArray;
4124 | var byteCount;
4125 | var byteIndex;
4126 | function utf8decode(byteString) {
4127 | byteArray = ucs2decode(byteString);
4128 | byteCount = byteArray.length;
4129 | byteIndex = 0;
4130 | var codePoints = [];
4131 | var tmp;
4132 | while ((tmp = decodeSymbol()) !== false) {
4133 | codePoints.push(tmp);
4134 | }
4135 | return ucs2encode(codePoints);
4136 | }
4137 |
4138 | /*--------------------------------------------------------------------------*/
4139 |
4140 | var utf8 = {
4141 | 'version': '2.0.0',
4142 | 'encode': utf8encode,
4143 | 'decode': utf8decode
4144 | };
4145 |
4146 | // Some AMD build optimizers, like r.js, check for specific condition patterns
4147 | // like the following:
4148 | if (
4149 | typeof define == 'function' &&
4150 | typeof define.amd == 'object' &&
4151 | define.amd
4152 | ) {
4153 | define(function() {
4154 | return utf8;
4155 | });
4156 | } else if (freeExports && !freeExports.nodeType) {
4157 | if (freeModule) { // in Node.js or RingoJS v0.8.0+
4158 | freeModule.exports = utf8;
4159 | } else { // in Narwhal or RingoJS v0.7.0-
4160 | var object = {};
4161 | var hasOwnProperty = object.hasOwnProperty;
4162 | for (var key in utf8) {
4163 | hasOwnProperty.call(utf8, key) && (freeExports[key] = utf8[key]);
4164 | }
4165 | }
4166 | } else { // in Rhino or a web browser
4167 | root.utf8 = utf8;
4168 | }
4169 |
4170 | }(this));
4171 |
4172 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {})
4173 | },{}],31:[function(_dereq_,module,exports){
4174 | 'use strict';
4175 |
4176 | var alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split('')
4177 | , length = 64
4178 | , map = {}
4179 | , seed = 0
4180 | , i = 0
4181 | , prev;
4182 |
4183 | /**
4184 | * Return a string representing the specified number.
4185 | *
4186 | * @param {Number} num The number to convert.
4187 | * @returns {String} The string representation of the number.
4188 | * @api public
4189 | */
4190 | function encode(num) {
4191 | var encoded = '';
4192 |
4193 | do {
4194 | encoded = alphabet[num % length] + encoded;
4195 | num = Math.floor(num / length);
4196 | } while (num > 0);
4197 |
4198 | return encoded;
4199 | }
4200 |
4201 | /**
4202 | * Return the integer value specified by the given string.
4203 | *
4204 | * @param {String} str The string to convert.
4205 | * @returns {Number} The integer value represented by the string.
4206 | * @api public
4207 | */
4208 | function decode(str) {
4209 | var decoded = 0;
4210 |
4211 | for (i = 0; i < str.length; i++) {
4212 | decoded = decoded * length + map[str.charAt(i)];
4213 | }
4214 |
4215 | return decoded;
4216 | }
4217 |
4218 | /**
4219 | * Yeast: A tiny growing id generator.
4220 | *
4221 | * @returns {String} A unique id.
4222 | * @api public
4223 | */
4224 | function yeast() {
4225 | var now = encode(+new Date());
4226 |
4227 | if (now !== prev) return seed = 0, prev = now;
4228 | return now +'.'+ encode(seed++);
4229 | }
4230 |
4231 | //
4232 | // Map each character to its index.
4233 | //
4234 | for (; i < length; i++) map[alphabet[i]] = i;
4235 |
4236 | //
4237 | // Expose the `yeast`, `encode` and `decode` functions.
4238 | //
4239 | yeast.encode = encode;
4240 | yeast.decode = decode;
4241 | module.exports = yeast;
4242 |
4243 | },{}]},{},[1])(1)
4244 | });
4245 |
--------------------------------------------------------------------------------