Commit | Line | Data |
---|---|---|
6ab4b1ba D |
1 | !function(e){if("object"==typeof exports)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);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ |
2 | ||
3 | module.exports = _dereq_('./lib/'); | |
4 | ||
5 | },{"./lib/":2}],2:[function(_dereq_,module,exports){ | |
6 | ||
7 | module.exports = _dereq_('./socket'); | |
d99e7823 | 8 | |
f8e5fb60 | 9 | /** |
6ab4b1ba | 10 | * Exports parser |
a74c0f0c | 11 | * |
a74c0f0c | 12 | * @api public |
6ab4b1ba | 13 | * |
f8e5fb60 | 14 | */ |
6ab4b1ba | 15 | module.exports.parser = _dereq_('engine.io-parser'); |
d99e7823 | 16 | |
e65c2242 | 17 | },{"./socket":3,"engine.io-parser":15}],3:[function(_dereq_,module,exports){ |
6ab4b1ba D |
18 | (function (global){ |
19 | /** | |
20 | * Module dependencies. | |
21 | */ | |
a74c0f0c | 22 | |
6ab4b1ba | 23 | var transports = _dereq_('./transports'); |
e65c2242 | 24 | var Emitter = _dereq_('component-emitter'); |
6ab4b1ba D |
25 | var debug = _dereq_('debug')('engine.io-client:socket'); |
26 | var index = _dereq_('indexof'); | |
27 | var parser = _dereq_('engine.io-parser'); | |
28 | var parseuri = _dereq_('parseuri'); | |
29 | var parsejson = _dereq_('parsejson'); | |
30 | var parseqs = _dereq_('parseqs'); | |
d99e7823 D |
31 | |
32 | /** | |
6ab4b1ba | 33 | * Module exports. |
d99e7823 D |
34 | */ |
35 | ||
6ab4b1ba | 36 | module.exports = Socket; |
d99e7823 D |
37 | |
38 | /** | |
6ab4b1ba D |
39 | * Noop function. |
40 | * | |
41 | * @api private | |
d99e7823 D |
42 | */ |
43 | ||
6ab4b1ba | 44 | function noop(){} |
d99e7823 D |
45 | |
46 | /** | |
6ab4b1ba | 47 | * Socket constructor. |
a74c0f0c | 48 | * |
6ab4b1ba D |
49 | * @param {String|Object} uri or options |
50 | * @param {Object} options | |
51 | * @api public | |
d99e7823 D |
52 | */ |
53 | ||
6ab4b1ba D |
54 | function Socket(uri, opts){ |
55 | if (!(this instanceof Socket)) return new Socket(uri, opts); | |
56 | ||
57 | opts = opts || {}; | |
d99e7823 | 58 | |
6ab4b1ba D |
59 | if (uri && 'object' == typeof uri) { |
60 | opts = uri; | |
61 | uri = null; | |
62 | } | |
d99e7823 | 63 | |
6ab4b1ba D |
64 | if (uri) { |
65 | uri = parseuri(uri); | |
66 | opts.host = uri.host; | |
67 | opts.secure = uri.protocol == 'https' || uri.protocol == 'wss'; | |
68 | opts.port = uri.port; | |
69 | if (uri.query) opts.query = uri.query; | |
a74c0f0c D |
70 | } |
71 | ||
6ab4b1ba D |
72 | this.secure = null != opts.secure ? opts.secure : |
73 | (global.location && 'https:' == location.protocol); | |
74 | ||
75 | if (opts.host) { | |
76 | var pieces = opts.host.split(':'); | |
77 | opts.hostname = pieces.shift(); | |
78 | if (pieces.length) opts.port = pieces.pop(); | |
a74c0f0c | 79 | } |
6ab4b1ba D |
80 | |
81 | this.agent = opts.agent || false; | |
82 | this.hostname = opts.hostname || | |
83 | (global.location ? location.hostname : 'localhost'); | |
84 | this.port = opts.port || (global.location && location.port ? | |
85 | location.port : | |
86 | (this.secure ? 443 : 80)); | |
87 | this.query = opts.query || {}; | |
88 | if ('string' == typeof this.query) this.query = parseqs.decode(this.query); | |
89 | this.upgrade = false !== opts.upgrade; | |
90 | this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; | |
91 | this.forceJSONP = !!opts.forceJSONP; | |
92 | this.forceBase64 = !!opts.forceBase64; | |
93 | this.timestampParam = opts.timestampParam || 't'; | |
94 | this.timestampRequests = opts.timestampRequests; | |
95 | this.transports = opts.transports || ['polling', 'websocket']; | |
96 | this.readyState = ''; | |
97 | this.writeBuffer = []; | |
98 | this.callbackBuffer = []; | |
99 | this.policyPort = opts.policyPort || 843; | |
100 | this.rememberUpgrade = opts.rememberUpgrade || false; | |
101 | this.open(); | |
102 | this.binaryType = null; | |
103 | this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; | |
104 | } | |
105 | ||
106 | Socket.priorWebsocketSuccess = false; | |
107 | ||
108 | /** | |
109 | * Mix in `Emitter`. | |
110 | */ | |
111 | ||
112 | Emitter(Socket.prototype); | |
d99e7823 D |
113 | |
114 | /** | |
6ab4b1ba | 115 | * Protocol version. |
d99e7823 | 116 | * |
6ab4b1ba D |
117 | * @api public |
118 | */ | |
119 | ||
120 | Socket.protocol = parser.protocol; // this is an int | |
121 | ||
122 | /** | |
123 | * Expose deps for legacy compatibility | |
124 | * and standalone browser access. | |
125 | */ | |
126 | ||
127 | Socket.Socket = Socket; | |
128 | Socket.Transport = _dereq_('./transport'); | |
129 | Socket.transports = _dereq_('./transports'); | |
130 | Socket.parser = _dereq_('engine.io-parser'); | |
131 | ||
132 | /** | |
133 | * Creates transport of the given type. | |
134 | * | |
135 | * @param {String} transport name | |
136 | * @return {Transport} | |
a74c0f0c | 137 | * @api private |
d99e7823 D |
138 | */ |
139 | ||
6ab4b1ba D |
140 | Socket.prototype.createTransport = function (name) { |
141 | debug('creating transport "%s"', name); | |
142 | var query = clone(this.query); | |
143 | ||
144 | // append engine.io protocol identifier | |
145 | query.EIO = parser.protocol; | |
146 | ||
147 | // transport name | |
148 | query.transport = name; | |
149 | ||
150 | // session id if we already have one | |
151 | if (this.id) query.sid = this.id; | |
d99e7823 | 152 | |
6ab4b1ba D |
153 | var transport = new transports[name]({ |
154 | agent: this.agent, | |
155 | hostname: this.hostname, | |
156 | port: this.port, | |
157 | secure: this.secure, | |
158 | path: this.path, | |
159 | query: query, | |
160 | forceJSONP: this.forceJSONP, | |
161 | forceBase64: this.forceBase64, | |
162 | timestampRequests: this.timestampRequests, | |
163 | timestampParam: this.timestampParam, | |
164 | policyPort: this.policyPort, | |
165 | socket: this | |
166 | }); | |
d99e7823 | 167 | |
6ab4b1ba D |
168 | return transport; |
169 | }; | |
d99e7823 | 170 | |
6ab4b1ba D |
171 | function clone (obj) { |
172 | var o = {}; | |
173 | for (var i in obj) { | |
174 | if (obj.hasOwnProperty(i)) { | |
175 | o[i] = obj[i]; | |
a74c0f0c D |
176 | } |
177 | } | |
6ab4b1ba D |
178 | return o; |
179 | } | |
d99e7823 D |
180 | |
181 | /** | |
6ab4b1ba | 182 | * Initializes transport to use and starts probe. |
a74c0f0c | 183 | * |
a74c0f0c | 184 | * @api private |
d99e7823 | 185 | */ |
6ab4b1ba D |
186 | Socket.prototype.open = function () { |
187 | var transport; | |
188 | if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) { | |
189 | transport = 'websocket'; | |
190 | } else { | |
191 | transport = this.transports[0]; | |
192 | } | |
193 | this.readyState = 'opening'; | |
194 | var transport = this.createTransport(transport); | |
195 | transport.open(); | |
196 | this.setTransport(transport); | |
a74c0f0c | 197 | }; |
d99e7823 D |
198 | |
199 | /** | |
6ab4b1ba | 200 | * Sets the current transport. Disables the existing one (if any). |
d99e7823 | 201 | * |
d99e7823 D |
202 | * @api private |
203 | */ | |
204 | ||
6ab4b1ba D |
205 | Socket.prototype.setTransport = function(transport){ |
206 | debug('setting transport %s', transport.name); | |
207 | var self = this; | |
208 | ||
209 | if (this.transport) { | |
210 | debug('clearing existing transport %s', this.transport.name); | |
211 | this.transport.removeAllListeners(); | |
a74c0f0c | 212 | } |
6ab4b1ba D |
213 | |
214 | // set up transport | |
215 | this.transport = transport; | |
216 | ||
217 | // set up transport listeners | |
218 | transport | |
219 | .on('drain', function(){ | |
220 | self.onDrain(); | |
221 | }) | |
222 | .on('packet', function(packet){ | |
223 | self.onPacket(packet); | |
224 | }) | |
225 | .on('error', function(e){ | |
226 | self.onError(e); | |
227 | }) | |
228 | .on('close', function(){ | |
229 | self.onClose('transport close'); | |
230 | }); | |
a74c0f0c | 231 | }; |
d99e7823 D |
232 | |
233 | /** | |
6ab4b1ba | 234 | * Probes a transport. |
d99e7823 | 235 | * |
6ab4b1ba | 236 | * @param {String} transport name |
a74c0f0c | 237 | * @api private |
d99e7823 D |
238 | */ |
239 | ||
6ab4b1ba D |
240 | Socket.prototype.probe = function (name) { |
241 | debug('probing transport "%s"', name); | |
242 | var transport = this.createTransport(name, { probe: 1 }) | |
243 | , failed = false | |
244 | , self = this; | |
d99e7823 | 245 | |
6ab4b1ba | 246 | Socket.priorWebsocketSuccess = false; |
d99e7823 | 247 | |
6ab4b1ba D |
248 | function onTransportOpen(){ |
249 | if (self.onlyBinaryUpgrades) { | |
250 | var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; | |
251 | failed = failed || upgradeLosesBinary; | |
a74c0f0c | 252 | } |
6ab4b1ba D |
253 | if (failed) return; |
254 | ||
255 | debug('probe transport "%s" opened', name); | |
256 | transport.send([{ type: 'ping', data: 'probe' }]); | |
257 | transport.once('packet', function (msg) { | |
258 | if (failed) return; | |
259 | if ('pong' == msg.type && 'probe' == msg.data) { | |
260 | debug('probe transport "%s" pong', name); | |
261 | self.upgrading = true; | |
262 | self.emit('upgrading', transport); | |
263 | Socket.priorWebsocketSuccess = 'websocket' == transport.name; | |
264 | ||
265 | debug('pausing current transport "%s"', self.transport.name); | |
266 | self.transport.pause(function () { | |
267 | if (failed) return; | |
268 | if ('closed' == self.readyState || 'closing' == self.readyState) { | |
269 | return; | |
270 | } | |
271 | debug('changing transport and sending upgrade packet'); | |
272 | ||
273 | cleanup(); | |
274 | ||
275 | self.setTransport(transport); | |
276 | transport.send([{ type: 'upgrade' }]); | |
277 | self.emit('upgrade', transport); | |
278 | transport = null; | |
279 | self.upgrading = false; | |
280 | self.flush(); | |
281 | }); | |
282 | } else { | |
283 | debug('probe transport "%s" failed', name); | |
284 | var err = new Error('probe error'); | |
285 | err.transport = transport.name; | |
286 | self.emit('upgradeError', err); | |
287 | } | |
288 | }); | |
289 | } | |
290 | ||
291 | function freezeTransport() { | |
292 | if (failed) return; | |
293 | ||
294 | // Any callback called by transport should be ignored since now | |
295 | failed = true; | |
296 | ||
297 | cleanup(); | |
298 | ||
299 | transport.close(); | |
300 | transport = null; | |
d99e7823 D |
301 | } |
302 | ||
6ab4b1ba D |
303 | //Handle any error that happens while probing |
304 | function onerror(err) { | |
305 | var error = new Error('probe error: ' + err); | |
306 | error.transport = transport.name; | |
307 | ||
308 | freezeTransport(); | |
309 | ||
310 | debug('probe transport "%s" failed because of error: %s', name, err); | |
a74c0f0c | 311 | |
6ab4b1ba | 312 | self.emit('upgradeError', error); |
d99e7823 D |
313 | } |
314 | ||
6ab4b1ba D |
315 | function onTransportClose(){ |
316 | onerror("transport closed"); | |
317 | } | |
d99e7823 | 318 | |
6ab4b1ba D |
319 | //When the socket is closed while we're probing |
320 | function onclose(){ | |
321 | onerror("socket closed"); | |
322 | } | |
d99e7823 | 323 | |
6ab4b1ba D |
324 | //When the socket is upgraded while we're probing |
325 | function onupgrade(to){ | |
326 | if (transport && to.name != transport.name) { | |
327 | debug('"%s" works - aborting "%s"', to.name, transport.name); | |
328 | freezeTransport(); | |
329 | } | |
330 | } | |
d99e7823 | 331 | |
6ab4b1ba D |
332 | //Remove all listeners on the transport and on self |
333 | function cleanup(){ | |
334 | transport.removeListener('open', onTransportOpen); | |
335 | transport.removeListener('error', onerror); | |
336 | transport.removeListener('close', onTransportClose); | |
337 | self.removeListener('close', onclose); | |
338 | self.removeListener('upgrading', onupgrade); | |
339 | } | |
d99e7823 | 340 | |
6ab4b1ba D |
341 | transport.once('open', onTransportOpen); |
342 | transport.once('error', onerror); | |
343 | transport.once('close', onTransportClose); | |
a74c0f0c | 344 | |
6ab4b1ba D |
345 | this.once('close', onclose); |
346 | this.once('upgrading', onupgrade); | |
d99e7823 | 347 | |
6ab4b1ba | 348 | transport.open(); |
d99e7823 | 349 | |
6ab4b1ba | 350 | }; |
d99e7823 D |
351 | |
352 | /** | |
6ab4b1ba | 353 | * Called when connection is deemed open. |
a74c0f0c D |
354 | * |
355 | * @api public | |
f8e5fb60 JA |
356 | */ |
357 | ||
6ab4b1ba D |
358 | Socket.prototype.onOpen = function () { |
359 | debug('socket open'); | |
360 | this.readyState = 'open'; | |
361 | Socket.priorWebsocketSuccess = 'websocket' == this.transport.name; | |
362 | this.emit('open'); | |
363 | this.flush(); | |
364 | ||
365 | // we check for `readyState` in case an `open` | |
366 | // listener already closed the socket | |
367 | if ('open' == this.readyState && this.upgrade && this.transport.pause) { | |
368 | debug('starting upgrade probes'); | |
369 | for (var i = 0, l = this.upgrades.length; i < l; i++) { | |
370 | this.probe(this.upgrades[i]); | |
371 | } | |
372 | } | |
a74c0f0c | 373 | }; |
f8e5fb60 JA |
374 | |
375 | /** | |
6ab4b1ba | 376 | * Handles a packet. |
d99e7823 | 377 | * |
d99e7823 D |
378 | * @api private |
379 | */ | |
380 | ||
6ab4b1ba D |
381 | Socket.prototype.onPacket = function (packet) { |
382 | if ('opening' == this.readyState || 'open' == this.readyState) { | |
383 | debug('socket receive: type "%s", data "%s"', packet.type, packet.data); | |
d99e7823 | 384 | |
6ab4b1ba | 385 | this.emit('packet', packet); |
d99e7823 | 386 | |
6ab4b1ba D |
387 | // Socket is live - any packet counts |
388 | this.emit('heartbeat'); | |
d99e7823 | 389 | |
6ab4b1ba D |
390 | switch (packet.type) { |
391 | case 'open': | |
392 | this.onHandshake(parsejson(packet.data)); | |
393 | break; | |
d99e7823 | 394 | |
6ab4b1ba D |
395 | case 'pong': |
396 | this.setPing(); | |
397 | break; | |
d99e7823 | 398 | |
6ab4b1ba D |
399 | case 'error': |
400 | var err = new Error('server error'); | |
401 | err.code = packet.data; | |
402 | this.emit('error', err); | |
403 | break; | |
d99e7823 | 404 | |
6ab4b1ba D |
405 | case 'message': |
406 | this.emit('data', packet.data); | |
407 | this.emit('message', packet.data); | |
408 | break; | |
409 | } | |
410 | } else { | |
411 | debug('packet received with socket readyState "%s"', this.readyState); | |
412 | } | |
d99e7823 D |
413 | }; |
414 | ||
415 | /** | |
6ab4b1ba | 416 | * Called upon handshake completion. |
d99e7823 | 417 | * |
6ab4b1ba D |
418 | * @param {Object} handshake obj |
419 | * @api private | |
d99e7823 D |
420 | */ |
421 | ||
6ab4b1ba D |
422 | Socket.prototype.onHandshake = function (data) { |
423 | this.emit('handshake', data); | |
424 | this.id = data.sid; | |
425 | this.transport.query.sid = data.sid; | |
426 | this.upgrades = this.filterUpgrades(data.upgrades); | |
427 | this.pingInterval = data.pingInterval; | |
428 | this.pingTimeout = data.pingTimeout; | |
429 | this.onOpen(); | |
430 | // In case open handler closes socket | |
431 | if ('closed' == this.readyState) return; | |
432 | this.setPing(); | |
d99e7823 | 433 | |
6ab4b1ba D |
434 | // Prolong liveness of socket on heartbeat |
435 | this.removeListener('heartbeat', this.onHeartbeat); | |
436 | this.on('heartbeat', this.onHeartbeat); | |
a74c0f0c | 437 | }; |
d99e7823 | 438 | |
a74c0f0c | 439 | /** |
6ab4b1ba | 440 | * Resets ping timeout. |
a74c0f0c | 441 | * |
6ab4b1ba | 442 | * @api private |
a74c0f0c | 443 | */ |
d99e7823 | 444 | |
6ab4b1ba D |
445 | Socket.prototype.onHeartbeat = function (timeout) { |
446 | clearTimeout(this.pingTimeoutTimer); | |
447 | var self = this; | |
448 | self.pingTimeoutTimer = setTimeout(function () { | |
449 | if ('closed' == self.readyState) return; | |
450 | self.onClose('ping timeout'); | |
451 | }, timeout || (self.pingInterval + self.pingTimeout)); | |
a74c0f0c | 452 | }; |
d99e7823 | 453 | |
a74c0f0c | 454 | /** |
6ab4b1ba D |
455 | * Pings server every `this.pingInterval` and expects response |
456 | * within `this.pingTimeout` or closes connection. | |
a74c0f0c | 457 | * |
6ab4b1ba | 458 | * @api private |
a74c0f0c | 459 | */ |
f8e5fb60 | 460 | |
6ab4b1ba D |
461 | Socket.prototype.setPing = function () { |
462 | var self = this; | |
463 | clearTimeout(self.pingIntervalTimer); | |
464 | self.pingIntervalTimer = setTimeout(function () { | |
465 | debug('writing ping packet - expecting pong within %sms', self.pingTimeout); | |
466 | self.ping(); | |
467 | self.onHeartbeat(self.pingTimeout); | |
468 | }, self.pingInterval); | |
d99e7823 D |
469 | }; |
470 | ||
471 | /** | |
6ab4b1ba D |
472 | * Sends a ping packet. |
473 | * | |
474 | * @api public | |
475 | */ | |
476 | ||
477 | Socket.prototype.ping = function () { | |
478 | this.sendPacket('ping'); | |
479 | }; | |
480 | ||
481 | /** | |
482 | * Called on `drain` event | |
d99e7823 | 483 | * |
6ab4b1ba | 484 | * @api private |
d99e7823 D |
485 | */ |
486 | ||
6ab4b1ba D |
487 | Socket.prototype.onDrain = function() { |
488 | for (var i = 0; i < this.prevBufferLen; i++) { | |
489 | if (this.callbackBuffer[i]) { | |
490 | this.callbackBuffer[i](); | |
491 | } | |
492 | } | |
d99e7823 | 493 | |
6ab4b1ba D |
494 | this.writeBuffer.splice(0, this.prevBufferLen); |
495 | this.callbackBuffer.splice(0, this.prevBufferLen); | |
a74c0f0c | 496 | |
6ab4b1ba D |
497 | // setting prevBufferLen = 0 is very important |
498 | // for example, when upgrading, upgrade packet is sent over, | |
499 | // and a nonzero prevBufferLen could cause problems on `drain` | |
500 | this.prevBufferLen = 0; | |
a74c0f0c | 501 | |
6ab4b1ba D |
502 | if (this.writeBuffer.length == 0) { |
503 | this.emit('drain'); | |
504 | } else { | |
505 | this.flush(); | |
d99e7823 | 506 | } |
d99e7823 | 507 | }; |
d99e7823 | 508 | |
a74c0f0c | 509 | /** |
6ab4b1ba D |
510 | * Flush write buffers. |
511 | * | |
512 | * @api private | |
a74c0f0c | 513 | */ |
d99e7823 | 514 | |
6ab4b1ba D |
515 | Socket.prototype.flush = function () { |
516 | if ('closed' != this.readyState && this.transport.writable && | |
517 | !this.upgrading && this.writeBuffer.length) { | |
518 | debug('flushing %d packets in socket', this.writeBuffer.length); | |
519 | this.transport.send(this.writeBuffer); | |
520 | // keep track of current length of writeBuffer | |
521 | // splice writeBuffer and callbackBuffer on `drain` | |
522 | this.prevBufferLen = this.writeBuffer.length; | |
523 | this.emit('flush'); | |
524 | } | |
a74c0f0c | 525 | }; |
d99e7823 | 526 | |
a74c0f0c | 527 | /** |
6ab4b1ba D |
528 | * Sends a message. |
529 | * | |
530 | * @param {String} message. | |
531 | * @param {Function} callback function. | |
532 | * @return {Socket} for chaining. | |
533 | * @api public | |
a74c0f0c | 534 | */ |
d99e7823 | 535 | |
6ab4b1ba D |
536 | Socket.prototype.write = |
537 | Socket.prototype.send = function (msg, fn) { | |
538 | this.sendPacket('message', msg, fn); | |
539 | return this; | |
540 | }; | |
d99e7823 D |
541 | |
542 | /** | |
6ab4b1ba | 543 | * Sends a packet. |
f8e5fb60 | 544 | * |
6ab4b1ba D |
545 | * @param {String} packet type. |
546 | * @param {String} data. | |
547 | * @param {Function} callback function. | |
f8e5fb60 | 548 | * @api private |
d99e7823 D |
549 | */ |
550 | ||
6ab4b1ba D |
551 | Socket.prototype.sendPacket = function (type, data, fn) { |
552 | var packet = { type: type, data: data }; | |
553 | this.emit('packetCreate', packet); | |
554 | this.writeBuffer.push(packet); | |
555 | this.callbackBuffer.push(fn); | |
556 | this.flush(); | |
f8e5fb60 | 557 | }; |
d99e7823 D |
558 | |
559 | /** | |
6ab4b1ba | 560 | * Closes the connection. |
d99e7823 | 561 | * |
f8e5fb60 | 562 | * @api private |
d99e7823 D |
563 | */ |
564 | ||
6ab4b1ba D |
565 | Socket.prototype.close = function () { |
566 | if ('opening' == this.readyState || 'open' == this.readyState) { | |
567 | this.onClose('forced close'); | |
568 | debug('socket closing - telling transport to close'); | |
569 | this.transport.close(); | |
a74c0f0c D |
570 | } |
571 | ||
6ab4b1ba | 572 | return this; |
f8e5fb60 | 573 | }; |
d99e7823 D |
574 | |
575 | /** | |
6ab4b1ba | 576 | * Called upon transport error |
a74c0f0c | 577 | * |
f8e5fb60 | 578 | * @api private |
d99e7823 D |
579 | */ |
580 | ||
6ab4b1ba D |
581 | Socket.prototype.onError = function (err) { |
582 | debug('socket error %j', err); | |
583 | Socket.priorWebsocketSuccess = false; | |
584 | this.emit('error', err); | |
585 | this.onClose('transport error', err); | |
f8e5fb60 | 586 | }; |
d99e7823 | 587 | |
6ab4b1ba D |
588 | /** |
589 | * Called upon transport close. | |
a74c0f0c | 590 | * |
6ab4b1ba | 591 | * @api private |
a74c0f0c D |
592 | */ |
593 | ||
6ab4b1ba D |
594 | Socket.prototype.onClose = function (reason, desc) { |
595 | if ('opening' == this.readyState || 'open' == this.readyState) { | |
596 | debug('socket close with reason: "%s"', reason); | |
597 | var self = this; | |
a74c0f0c | 598 | |
6ab4b1ba D |
599 | // clear timers |
600 | clearTimeout(this.pingIntervalTimer); | |
601 | clearTimeout(this.pingTimeoutTimer); | |
a74c0f0c | 602 | |
6ab4b1ba D |
603 | // clean buffers in next tick, so developers can still |
604 | // grab the buffers on `close` event | |
605 | setTimeout(function() { | |
606 | self.writeBuffer = []; | |
607 | self.callbackBuffer = []; | |
608 | self.prevBufferLen = 0; | |
609 | }, 0); | |
a74c0f0c | 610 | |
6ab4b1ba D |
611 | // stop event from firing again for transport |
612 | this.transport.removeAllListeners('close'); | |
a74c0f0c | 613 | |
6ab4b1ba D |
614 | // ensure transport won't stay open |
615 | this.transport.close(); | |
a74c0f0c | 616 | |
6ab4b1ba D |
617 | // ignore further transport communication |
618 | this.transport.removeAllListeners(); | |
a74c0f0c | 619 | |
6ab4b1ba D |
620 | // set ready state |
621 | this.readyState = 'closed'; | |
a74c0f0c | 622 | |
6ab4b1ba D |
623 | // clear session id |
624 | this.id = null; | |
a74c0f0c | 625 | |
6ab4b1ba D |
626 | // emit close event |
627 | this.emit('close', reason, desc); | |
a74c0f0c | 628 | } |
f8e5fb60 | 629 | }; |
d99e7823 D |
630 | |
631 | /** | |
6ab4b1ba | 632 | * Filters upgrades, returning only those matching client transports. |
d99e7823 | 633 | * |
6ab4b1ba | 634 | * @param {Array} server upgrades |
d99e7823 | 635 | * @api private |
6ab4b1ba | 636 | * |
d99e7823 D |
637 | */ |
638 | ||
6ab4b1ba D |
639 | Socket.prototype.filterUpgrades = function (upgrades) { |
640 | var filteredUpgrades = []; | |
641 | for (var i = 0, j = upgrades.length; i<j; i++) { | |
642 | if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]); | |
f8e5fb60 | 643 | } |
6ab4b1ba | 644 | return filteredUpgrades; |
a74c0f0c | 645 | }; |
d99e7823 | 646 | |
6ab4b1ba | 647 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) |
e65c2242 | 648 | },{"./transport":4,"./transports":5,"component-emitter":12,"debug":14,"engine.io-parser":15,"indexof":23,"parsejson":24,"parseqs":25,"parseuri":26}],4:[function(_dereq_,module,exports){ |
6ab4b1ba D |
649 | /** |
650 | * Module dependencies. | |
651 | */ | |
d99e7823 | 652 | |
6ab4b1ba | 653 | var parser = _dereq_('engine.io-parser'); |
e65c2242 | 654 | var Emitter = _dereq_('component-emitter'); |
d99e7823 | 655 | |
a74c0f0c | 656 | /** |
6ab4b1ba | 657 | * Module exports. |
a74c0f0c D |
658 | */ |
659 | ||
6ab4b1ba | 660 | module.exports = Transport; |
d99e7823 | 661 | |
f8e5fb60 | 662 | /** |
6ab4b1ba | 663 | * Transport abstract constructor. |
f8e5fb60 | 664 | * |
6ab4b1ba D |
665 | * @param {Object} options. |
666 | * @api private | |
f8e5fb60 | 667 | */ |
d99e7823 | 668 | |
6ab4b1ba D |
669 | function Transport (opts) { |
670 | this.path = opts.path; | |
671 | this.hostname = opts.hostname; | |
672 | this.port = opts.port; | |
673 | this.secure = opts.secure; | |
674 | this.query = opts.query; | |
675 | this.timestampParam = opts.timestampParam; | |
676 | this.timestampRequests = opts.timestampRequests; | |
677 | this.readyState = ''; | |
678 | this.agent = opts.agent || false; | |
679 | this.socket = opts.socket; | |
680 | } | |
a74c0f0c | 681 | |
6ab4b1ba D |
682 | /** |
683 | * Mix in `Emitter`. | |
684 | */ | |
a74c0f0c | 685 | |
6ab4b1ba | 686 | Emitter(Transport.prototype); |
d99e7823 | 687 | |
f8e5fb60 | 688 | /** |
6ab4b1ba D |
689 | * A counter used to prevent collisions in the timestamps used |
690 | * for cache busting. | |
a74c0f0c D |
691 | */ |
692 | ||
6ab4b1ba | 693 | Transport.timestamps = 0; |
a74c0f0c D |
694 | |
695 | /** | |
6ab4b1ba | 696 | * Emits an error. |
f8e5fb60 | 697 | * |
6ab4b1ba D |
698 | * @param {String} str |
699 | * @return {Transport} for chaining | |
f8e5fb60 JA |
700 | * @api public |
701 | */ | |
702 | ||
6ab4b1ba D |
703 | Transport.prototype.onError = function (msg, desc) { |
704 | var err = new Error(msg); | |
705 | err.type = 'TransportError'; | |
706 | err.description = desc; | |
707 | this.emit('error', err); | |
708 | return this; | |
f8e5fb60 | 709 | }; |
d99e7823 D |
710 | |
711 | /** | |
6ab4b1ba | 712 | * Opens the transport. |
d99e7823 | 713 | * |
a74c0f0c | 714 | * @api public |
d99e7823 D |
715 | */ |
716 | ||
6ab4b1ba D |
717 | Transport.prototype.open = function () { |
718 | if ('closed' == this.readyState || '' == this.readyState) { | |
719 | this.readyState = 'opening'; | |
720 | this.doOpen(); | |
721 | } | |
722 | ||
723 | return this; | |
d99e7823 D |
724 | }; |
725 | ||
726 | /** | |
6ab4b1ba | 727 | * Closes the transport. |
d99e7823 D |
728 | * |
729 | * @api private | |
730 | */ | |
731 | ||
6ab4b1ba D |
732 | Transport.prototype.close = function () { |
733 | if ('opening' == this.readyState || 'open' == this.readyState) { | |
734 | this.doClose(); | |
735 | this.onClose(); | |
736 | } | |
d99e7823 | 737 | |
6ab4b1ba | 738 | return this; |
f8e5fb60 | 739 | }; |
d99e7823 | 740 | |
f8e5fb60 | 741 | /** |
6ab4b1ba | 742 | * Sends multiple packets. |
f8e5fb60 | 743 | * |
6ab4b1ba D |
744 | * @param {Array} packets |
745 | * @api private | |
f8e5fb60 JA |
746 | */ |
747 | ||
6ab4b1ba D |
748 | Transport.prototype.send = function(packets){ |
749 | if ('open' == this.readyState) { | |
750 | this.write(packets); | |
751 | } else { | |
752 | throw new Error('Transport not open'); | |
a74c0f0c | 753 | } |
d99e7823 D |
754 | }; |
755 | ||
756 | /** | |
6ab4b1ba D |
757 | * Called upon open |
758 | * | |
759 | * @api private | |
d99e7823 D |
760 | */ |
761 | ||
6ab4b1ba D |
762 | Transport.prototype.onOpen = function () { |
763 | this.readyState = 'open'; | |
764 | this.writable = true; | |
765 | this.emit('open'); | |
766 | }; | |
f8e5fb60 JA |
767 | |
768 | /** | |
6ab4b1ba | 769 | * Called with data. |
f8e5fb60 | 770 | * |
6ab4b1ba D |
771 | * @param {String} data |
772 | * @api private | |
f8e5fb60 | 773 | */ |
d99e7823 | 774 | |
e65c2242 JA |
775 | Transport.prototype.onData = function(data){ |
776 | try { | |
777 | var packet = parser.decodePacket(data, this.socket.binaryType); | |
778 | this.onPacket(packet); | |
779 | } catch(e){ | |
780 | e.data = data; | |
781 | this.onError('parser decode error', e); | |
782 | } | |
6ab4b1ba D |
783 | }; |
784 | ||
f8e5fb60 | 785 | /** |
6ab4b1ba | 786 | * Called with a decoded packet. |
f8e5fb60 | 787 | */ |
d99e7823 | 788 | |
6ab4b1ba D |
789 | Transport.prototype.onPacket = function (packet) { |
790 | this.emit('packet', packet); | |
791 | }; | |
d99e7823 | 792 | |
f8e5fb60 | 793 | /** |
6ab4b1ba D |
794 | * Called upon close. |
795 | * | |
796 | * @api private | |
f8e5fb60 | 797 | */ |
d99e7823 | 798 | |
6ab4b1ba D |
799 | Transport.prototype.onClose = function () { |
800 | this.readyState = 'closed'; | |
801 | this.emit('close'); | |
802 | }; | |
d99e7823 | 803 | |
e65c2242 | 804 | },{"component-emitter":12,"engine.io-parser":15}],5:[function(_dereq_,module,exports){ |
6ab4b1ba | 805 | (function (global){ |
f8e5fb60 | 806 | /** |
6ab4b1ba | 807 | * Module dependencies |
f8e5fb60 | 808 | */ |
d99e7823 | 809 | |
6ab4b1ba D |
810 | var XMLHttpRequest = _dereq_('xmlhttprequest'); |
811 | var XHR = _dereq_('./polling-xhr'); | |
812 | var JSONP = _dereq_('./polling-jsonp'); | |
813 | var websocket = _dereq_('./websocket'); | |
d99e7823 | 814 | |
f8e5fb60 | 815 | /** |
6ab4b1ba | 816 | * Export transports. |
f8e5fb60 JA |
817 | */ |
818 | ||
6ab4b1ba D |
819 | exports.polling = polling; |
820 | exports.websocket = websocket; | |
d99e7823 D |
821 | |
822 | /** | |
6ab4b1ba D |
823 | * Polling transport polymorphic constructor. |
824 | * Decides on xhr vs jsonp based on feature detection. | |
d99e7823 | 825 | * |
6ab4b1ba | 826 | * @api private |
d99e7823 D |
827 | */ |
828 | ||
6ab4b1ba D |
829 | function polling(opts){ |
830 | var xhr; | |
831 | var xd = false; | |
a74c0f0c | 832 | |
6ab4b1ba D |
833 | if (global.location) { |
834 | var isSSL = 'https:' == location.protocol; | |
835 | var port = location.port; | |
a74c0f0c | 836 | |
6ab4b1ba D |
837 | // some user agents have empty `location.port` |
838 | if (!port) { | |
839 | port = isSSL ? 443 : 80; | |
840 | } | |
a74c0f0c | 841 | |
6ab4b1ba | 842 | xd = opts.hostname != location.hostname || port != opts.port; |
a74c0f0c D |
843 | } |
844 | ||
6ab4b1ba D |
845 | opts.xdomain = xd; |
846 | xhr = new XMLHttpRequest(opts); | |
a74c0f0c | 847 | |
6ab4b1ba D |
848 | if ('open' in xhr && !opts.forceJSONP) { |
849 | return new XHR(opts); | |
850 | } else { | |
851 | return new JSONP(opts); | |
a74c0f0c | 852 | } |
6ab4b1ba | 853 | } |
a74c0f0c | 854 | |
6ab4b1ba D |
855 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) |
856 | },{"./polling-jsonp":6,"./polling-xhr":7,"./websocket":9,"xmlhttprequest":10}],6:[function(_dereq_,module,exports){ | |
857 | (function (global){ | |
a74c0f0c | 858 | |
6ab4b1ba D |
859 | /** |
860 | * Module requirements. | |
861 | */ | |
862 | ||
863 | var Polling = _dereq_('./polling'); | |
e65c2242 | 864 | var inherit = _dereq_('component-inherit'); |
d99e7823 | 865 | |
f8e5fb60 | 866 | /** |
6ab4b1ba | 867 | * Module exports. |
a74c0f0c D |
868 | */ |
869 | ||
6ab4b1ba | 870 | module.exports = JSONPPolling; |
a74c0f0c D |
871 | |
872 | /** | |
6ab4b1ba | 873 | * Cached regular expressions. |
f8e5fb60 JA |
874 | */ |
875 | ||
6ab4b1ba D |
876 | var rNewline = /\n/g; |
877 | var rEscapedNewline = /\\n/g; | |
878 | ||
879 | /** | |
880 | * Global JSONP callbacks. | |
881 | */ | |
882 | ||
883 | var callbacks; | |
d99e7823 D |
884 | |
885 | /** | |
6ab4b1ba | 886 | * Callbacks count. |
d99e7823 D |
887 | */ |
888 | ||
6ab4b1ba | 889 | var index = 0; |
d99e7823 | 890 | |
a74c0f0c | 891 | /** |
6ab4b1ba | 892 | * Noop. |
a74c0f0c D |
893 | */ |
894 | ||
6ab4b1ba | 895 | function empty () { } |
d99e7823 | 896 | |
f8e5fb60 | 897 | /** |
6ab4b1ba | 898 | * JSONP Polling constructor. |
f8e5fb60 | 899 | * |
6ab4b1ba D |
900 | * @param {Object} opts. |
901 | * @api public | |
f8e5fb60 | 902 | */ |
d99e7823 | 903 | |
6ab4b1ba D |
904 | function JSONPPolling (opts) { |
905 | Polling.call(this, opts); | |
a74c0f0c | 906 | |
6ab4b1ba | 907 | this.query = this.query || {}; |
a74c0f0c | 908 | |
6ab4b1ba D |
909 | // define global callbacks array if not present |
910 | // we do this here (lazily) to avoid unneeded global pollution | |
911 | if (!callbacks) { | |
912 | // we need to consider multiple engines in the same page | |
913 | if (!global.___eio) global.___eio = []; | |
914 | callbacks = global.___eio; | |
915 | } | |
a74c0f0c | 916 | |
6ab4b1ba D |
917 | // callback identifier |
918 | this.index = callbacks.length; | |
a74c0f0c | 919 | |
6ab4b1ba D |
920 | // add callback to jsonp global |
921 | var self = this; | |
922 | callbacks.push(function (msg) { | |
923 | self.onData(msg); | |
a74c0f0c D |
924 | }); |
925 | ||
6ab4b1ba D |
926 | // append to query string |
927 | this.query.j = this.index; | |
d99e7823 | 928 | |
6ab4b1ba D |
929 | // prevent spurious errors from being emitted when the window is unloaded |
930 | if (global.document && global.addEventListener) { | |
931 | global.addEventListener('beforeunload', function () { | |
932 | if (self.script) self.script.onerror = empty; | |
933 | }); | |
a74c0f0c | 934 | } |
a74c0f0c D |
935 | } |
936 | ||
d99e7823 | 937 | /** |
6ab4b1ba D |
938 | * Inherits from Polling. |
939 | */ | |
940 | ||
941 | inherit(JSONPPolling, Polling); | |
942 | ||
943 | /* | |
944 | * JSONP only supports binary as base64 encoded strings | |
945 | */ | |
946 | ||
947 | JSONPPolling.prototype.supportsBinary = false; | |
948 | ||
949 | /** | |
950 | * Closes the socket. | |
d99e7823 | 951 | * |
d99e7823 D |
952 | * @api private |
953 | */ | |
954 | ||
6ab4b1ba D |
955 | JSONPPolling.prototype.doClose = function () { |
956 | if (this.script) { | |
957 | this.script.parentNode.removeChild(this.script); | |
958 | this.script = null; | |
959 | } | |
960 | ||
961 | if (this.form) { | |
962 | this.form.parentNode.removeChild(this.form); | |
963 | this.form = null; | |
964 | } | |
965 | ||
966 | Polling.prototype.doClose.call(this); | |
d99e7823 D |
967 | }; |
968 | ||
969 | /** | |
6ab4b1ba | 970 | * Starts a poll cycle. |
d99e7823 D |
971 | * |
972 | * @api private | |
973 | */ | |
974 | ||
6ab4b1ba | 975 | JSONPPolling.prototype.doPoll = function () { |
a74c0f0c | 976 | var self = this; |
6ab4b1ba | 977 | var script = document.createElement('script'); |
d99e7823 | 978 | |
6ab4b1ba D |
979 | if (this.script) { |
980 | this.script.parentNode.removeChild(this.script); | |
981 | this.script = null; | |
a74c0f0c | 982 | } |
f8e5fb60 | 983 | |
6ab4b1ba D |
984 | script.async = true; |
985 | script.src = this.uri(); | |
986 | script.onerror = function(e){ | |
987 | self.onError('jsonp poll error',e); | |
988 | }; | |
a74c0f0c | 989 | |
6ab4b1ba D |
990 | var insertAt = document.getElementsByTagName('script')[0]; |
991 | insertAt.parentNode.insertBefore(script, insertAt); | |
992 | this.script = script; | |
993 | ||
994 | var isUAgecko = 'undefined' != typeof navigator && /gecko/i.test(navigator.userAgent); | |
e65c2242 | 995 | |
6ab4b1ba D |
996 | if (isUAgecko) { |
997 | setTimeout(function () { | |
998 | var iframe = document.createElement('iframe'); | |
999 | document.body.appendChild(iframe); | |
1000 | document.body.removeChild(iframe); | |
1001 | }, 100); | |
1002 | } | |
f8e5fb60 JA |
1003 | }; |
1004 | ||
1005 | /** | |
6ab4b1ba | 1006 | * Writes with a hidden iframe. |
d99e7823 | 1007 | * |
6ab4b1ba D |
1008 | * @param {String} data to send |
1009 | * @param {Function} called upon flush. | |
d99e7823 D |
1010 | * @api private |
1011 | */ | |
1012 | ||
6ab4b1ba D |
1013 | JSONPPolling.prototype.doWrite = function (data, fn) { |
1014 | var self = this; | |
f8e5fb60 | 1015 | |
6ab4b1ba D |
1016 | if (!this.form) { |
1017 | var form = document.createElement('form'); | |
1018 | var area = document.createElement('textarea'); | |
1019 | var id = this.iframeId = 'eio_iframe_' + this.index; | |
1020 | var iframe; | |
d99e7823 | 1021 | |
6ab4b1ba D |
1022 | form.className = 'socketio'; |
1023 | form.style.position = 'absolute'; | |
1024 | form.style.top = '-1000px'; | |
1025 | form.style.left = '-1000px'; | |
1026 | form.target = id; | |
1027 | form.method = 'POST'; | |
1028 | form.setAttribute('accept-charset', 'utf-8'); | |
1029 | area.name = 'd'; | |
1030 | form.appendChild(area); | |
1031 | document.body.appendChild(form); | |
d99e7823 | 1032 | |
6ab4b1ba D |
1033 | this.form = form; |
1034 | this.area = area; | |
a74c0f0c | 1035 | } |
f8e5fb60 | 1036 | |
6ab4b1ba | 1037 | this.form.action = this.uri(); |
f8e5fb60 | 1038 | |
6ab4b1ba D |
1039 | function complete () { |
1040 | initIframe(); | |
1041 | fn(); | |
a74c0f0c | 1042 | } |
d99e7823 | 1043 | |
6ab4b1ba D |
1044 | function initIframe () { |
1045 | if (self.iframe) { | |
1046 | try { | |
1047 | self.form.removeChild(self.iframe); | |
1048 | } catch (e) { | |
1049 | self.onError('jsonp polling iframe removal error', e); | |
1050 | } | |
a74c0f0c | 1051 | } |
a74c0f0c | 1052 | |
6ab4b1ba D |
1053 | try { |
1054 | // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) | |
1055 | var html = '<iframe src="javascript:0" name="'+ self.iframeId +'">'; | |
1056 | iframe = document.createElement(html); | |
1057 | } catch (e) { | |
1058 | iframe = document.createElement('iframe'); | |
1059 | iframe.name = self.iframeId; | |
1060 | iframe.src = 'javascript:0'; | |
1061 | } | |
d99e7823 | 1062 | |
6ab4b1ba | 1063 | iframe.id = self.iframeId; |
d99e7823 | 1064 | |
6ab4b1ba D |
1065 | self.form.appendChild(iframe); |
1066 | self.iframe = iframe; | |
a74c0f0c | 1067 | } |
d99e7823 | 1068 | |
6ab4b1ba | 1069 | initIframe(); |
f8e5fb60 | 1070 | |
6ab4b1ba D |
1071 | // escape \n to prevent it from being converted into \r\n by some UAs |
1072 | // double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side | |
1073 | data = data.replace(rEscapedNewline, '\\\n'); | |
1074 | this.area.value = data.replace(rNewline, '\\n'); | |
f8e5fb60 | 1075 | |
6ab4b1ba D |
1076 | try { |
1077 | this.form.submit(); | |
1078 | } catch(e) {} | |
d99e7823 | 1079 | |
6ab4b1ba D |
1080 | if (this.iframe.attachEvent) { |
1081 | this.iframe.onreadystatechange = function(){ | |
1082 | if (self.iframe.readyState == 'complete') { | |
1083 | complete(); | |
1084 | } | |
1085 | }; | |
1086 | } else { | |
1087 | this.iframe.onload = complete; | |
a74c0f0c D |
1088 | } |
1089 | }; | |
d99e7823 | 1090 | |
6ab4b1ba | 1091 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) |
e65c2242 | 1092 | },{"./polling":8,"component-inherit":13}],7:[function(_dereq_,module,exports){ |
6ab4b1ba | 1093 | (function (global){ |
d99e7823 | 1094 | /** |
6ab4b1ba | 1095 | * Module requirements. |
d99e7823 D |
1096 | */ |
1097 | ||
6ab4b1ba D |
1098 | var XMLHttpRequest = _dereq_('xmlhttprequest'); |
1099 | var Polling = _dereq_('./polling'); | |
e65c2242 JA |
1100 | var Emitter = _dereq_('component-emitter'); |
1101 | var inherit = _dereq_('component-inherit'); | |
6ab4b1ba | 1102 | var debug = _dereq_('debug')('engine.io-client:polling-xhr'); |
d99e7823 D |
1103 | |
1104 | /** | |
6ab4b1ba | 1105 | * Module exports. |
a74c0f0c D |
1106 | */ |
1107 | ||
6ab4b1ba D |
1108 | module.exports = XHR; |
1109 | module.exports.Request = Request; | |
a74c0f0c D |
1110 | |
1111 | /** | |
6ab4b1ba | 1112 | * Empty function |
d99e7823 D |
1113 | */ |
1114 | ||
6ab4b1ba | 1115 | function empty(){} |
d99e7823 D |
1116 | |
1117 | /** | |
6ab4b1ba | 1118 | * XHR Polling constructor. |
d99e7823 | 1119 | * |
6ab4b1ba D |
1120 | * @param {Object} opts |
1121 | * @api public | |
d99e7823 D |
1122 | */ |
1123 | ||
6ab4b1ba D |
1124 | function XHR(opts){ |
1125 | Polling.call(this, opts); | |
a74c0f0c | 1126 | |
6ab4b1ba D |
1127 | if (global.location) { |
1128 | var isSSL = 'https:' == location.protocol; | |
1129 | var port = location.port; | |
a74c0f0c | 1130 | |
6ab4b1ba D |
1131 | // some user agents have empty `location.port` |
1132 | if (!port) { | |
1133 | port = isSSL ? 443 : 80; | |
1134 | } | |
a74c0f0c | 1135 | |
6ab4b1ba D |
1136 | this.xd = opts.hostname != global.location.hostname || |
1137 | port != opts.port; | |
a74c0f0c | 1138 | } |
6ab4b1ba | 1139 | } |
a74c0f0c | 1140 | |
6ab4b1ba D |
1141 | /** |
1142 | * Inherits from Polling. | |
1143 | */ | |
1144 | ||
1145 | inherit(XHR, Polling); | |
d99e7823 D |
1146 | |
1147 | /** | |
6ab4b1ba | 1148 | * XHR supports binary |
d99e7823 D |
1149 | */ |
1150 | ||
6ab4b1ba | 1151 | XHR.prototype.supportsBinary = true; |
d99e7823 D |
1152 | |
1153 | /** | |
6ab4b1ba | 1154 | * Creates a request. |
a74c0f0c | 1155 | * |
6ab4b1ba | 1156 | * @param {String} method |
a74c0f0c | 1157 | * @api private |
d99e7823 D |
1158 | */ |
1159 | ||
6ab4b1ba D |
1160 | XHR.prototype.request = function(opts){ |
1161 | opts = opts || {}; | |
1162 | opts.uri = this.uri(); | |
1163 | opts.xd = this.xd; | |
1164 | opts.agent = this.agent || false; | |
1165 | opts.supportsBinary = this.supportsBinary; | |
1166 | return new Request(opts); | |
1167 | }; | |
d99e7823 D |
1168 | |
1169 | /** | |
6ab4b1ba | 1170 | * Sends data. |
d99e7823 | 1171 | * |
6ab4b1ba D |
1172 | * @param {String} data to send. |
1173 | * @param {Function} called upon flush. | |
d99e7823 D |
1174 | * @api private |
1175 | */ | |
1176 | ||
6ab4b1ba D |
1177 | XHR.prototype.doWrite = function(data, fn){ |
1178 | var isBinary = typeof data !== 'string' && data !== undefined; | |
1179 | var req = this.request({ method: 'POST', data: data, isBinary: isBinary }); | |
1180 | var self = this; | |
1181 | req.on('success', fn); | |
1182 | req.on('error', function(err){ | |
1183 | self.onError('xhr post error', err); | |
1184 | }); | |
1185 | this.sendXhr = req; | |
1186 | }; | |
d99e7823 | 1187 | |
a74c0f0c | 1188 | /** |
6ab4b1ba | 1189 | * Starts a poll cycle. |
a74c0f0c D |
1190 | * |
1191 | * @api private | |
1192 | */ | |
d99e7823 | 1193 | |
6ab4b1ba D |
1194 | XHR.prototype.doPoll = function(){ |
1195 | debug('xhr poll'); | |
1196 | var req = this.request(); | |
1197 | var self = this; | |
1198 | req.on('data', function(data){ | |
1199 | self.onData(data); | |
1200 | }); | |
1201 | req.on('error', function(err){ | |
1202 | self.onError('xhr poll error', err); | |
1203 | }); | |
1204 | this.pollXhr = req; | |
1205 | }; | |
d99e7823 | 1206 | |
a74c0f0c | 1207 | /** |
6ab4b1ba D |
1208 | * Request constructor |
1209 | * | |
1210 | * @param {Object} options | |
1211 | * @api public | |
a74c0f0c | 1212 | */ |
d99e7823 | 1213 | |
6ab4b1ba D |
1214 | function Request(opts){ |
1215 | this.method = opts.method || 'GET'; | |
1216 | this.uri = opts.uri; | |
1217 | this.xd = !!opts.xd; | |
1218 | this.async = false !== opts.async; | |
1219 | this.data = undefined != opts.data ? opts.data : null; | |
1220 | this.agent = opts.agent; | |
1221 | this.create(opts.isBinary, opts.supportsBinary); | |
1222 | } | |
d99e7823 | 1223 | |
a74c0f0c | 1224 | /** |
6ab4b1ba | 1225 | * Mix in `Emitter`. |
a74c0f0c | 1226 | */ |
d99e7823 | 1227 | |
6ab4b1ba | 1228 | Emitter(Request.prototype); |
d99e7823 | 1229 | |
a74c0f0c | 1230 | /** |
6ab4b1ba | 1231 | * Creates the XHR object and sends the request. |
a74c0f0c | 1232 | * |
a74c0f0c D |
1233 | * @api private |
1234 | */ | |
1235 | ||
6ab4b1ba D |
1236 | Request.prototype.create = function(isBinary, supportsBinary){ |
1237 | var xhr = this.xhr = new XMLHttpRequest({ agent: this.agent, xdomain: this.xd }); | |
1238 | var self = this; | |
f8e5fb60 | 1239 | |
a74c0f0c | 1240 | try { |
6ab4b1ba D |
1241 | debug('xhr open %s: %s', this.method, this.uri); |
1242 | xhr.open(this.method, this.uri, this.async); | |
1243 | if (supportsBinary) { | |
1244 | // This has to be done after open because Firefox is stupid | |
1245 | // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension | |
1246 | xhr.responseType = 'arraybuffer'; | |
a74c0f0c | 1247 | } |
a74c0f0c | 1248 | |
6ab4b1ba D |
1249 | if ('POST' == this.method) { |
1250 | try { | |
1251 | if (isBinary) { | |
1252 | xhr.setRequestHeader('Content-type', 'application/octet-stream'); | |
1253 | } else { | |
1254 | xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); | |
1255 | } | |
1256 | } catch (e) {} | |
1257 | } | |
d99e7823 | 1258 | |
6ab4b1ba D |
1259 | // ie6 check |
1260 | if ('withCredentials' in xhr) { | |
1261 | xhr.withCredentials = true; | |
1262 | } | |
d99e7823 | 1263 | |
6ab4b1ba D |
1264 | xhr.onreadystatechange = function(){ |
1265 | var data; | |
a74c0f0c | 1266 | |
6ab4b1ba D |
1267 | try { |
1268 | if (4 != xhr.readyState) return; | |
1269 | if (200 == xhr.status || 1223 == xhr.status) { | |
1270 | var contentType = xhr.getResponseHeader('Content-Type'); | |
1271 | if (contentType === 'application/octet-stream') { | |
1272 | data = xhr.response; | |
1273 | } else { | |
1274 | if (!supportsBinary) { | |
1275 | data = xhr.responseText; | |
1276 | } else { | |
1277 | data = 'ok'; | |
1278 | } | |
1279 | } | |
1280 | } else { | |
1281 | // make sure the `error` event handler that's user-set | |
1282 | // does not throw in the same tick and gets caught here | |
1283 | setTimeout(function(){ | |
1284 | self.onError(xhr.status); | |
1285 | }, 0); | |
1286 | } | |
1287 | } catch (e) { | |
1288 | self.onError(e); | |
1289 | } | |
a74c0f0c | 1290 | |
6ab4b1ba D |
1291 | if (null != data) { |
1292 | self.onData(data); | |
1293 | } | |
1294 | }; | |
a74c0f0c | 1295 | |
6ab4b1ba D |
1296 | debug('xhr data %s', this.data); |
1297 | xhr.send(this.data); | |
1298 | } catch (e) { | |
1299 | // Need to defer since .create() is called directly fhrom the constructor | |
1300 | // and thus the 'error' event can only be only bound *after* this exception | |
1301 | // occurs. Therefore, also, we cannot throw here at all. | |
1302 | setTimeout(function() { | |
1303 | self.onError(e); | |
1304 | }, 0); | |
1305 | return; | |
a74c0f0c D |
1306 | } |
1307 | ||
6ab4b1ba D |
1308 | if (global.document) { |
1309 | this.index = Request.requestsCount++; | |
1310 | Request.requests[this.index] = this; | |
1311 | } | |
f8e5fb60 | 1312 | }; |
d99e7823 D |
1313 | |
1314 | /** | |
6ab4b1ba | 1315 | * Called upon successful response. |
f8e5fb60 JA |
1316 | * |
1317 | * @api private | |
d99e7823 D |
1318 | */ |
1319 | ||
6ab4b1ba D |
1320 | Request.prototype.onSuccess = function(){ |
1321 | this.emit('success'); | |
1322 | this.cleanup(); | |
f8e5fb60 | 1323 | }; |
d99e7823 D |
1324 | |
1325 | /** | |
6ab4b1ba | 1326 | * Called if we have data. |
f8e5fb60 JA |
1327 | * |
1328 | * @api private | |
d99e7823 D |
1329 | */ |
1330 | ||
6ab4b1ba D |
1331 | Request.prototype.onData = function(data){ |
1332 | this.emit('data', data); | |
1333 | this.onSuccess(); | |
f8e5fb60 | 1334 | }; |
d99e7823 D |
1335 | |
1336 | /** | |
6ab4b1ba D |
1337 | * Called upon error. |
1338 | * | |
1339 | * @api private | |
d99e7823 D |
1340 | */ |
1341 | ||
6ab4b1ba D |
1342 | Request.prototype.onError = function(err){ |
1343 | this.emit('error', err); | |
1344 | this.cleanup(); | |
1345 | }; | |
d99e7823 | 1346 | |
a74c0f0c | 1347 | /** |
6ab4b1ba D |
1348 | * Cleans up house. |
1349 | * | |
1350 | * @api private | |
a74c0f0c | 1351 | */ |
d99e7823 | 1352 | |
6ab4b1ba D |
1353 | Request.prototype.cleanup = function(){ |
1354 | if ('undefined' == typeof this.xhr || null === this.xhr) { | |
1355 | return; | |
1356 | } | |
1357 | // xmlhttprequest | |
1358 | this.xhr.onreadystatechange = empty; | |
d99e7823 | 1359 | |
6ab4b1ba D |
1360 | try { |
1361 | this.xhr.abort(); | |
1362 | } catch(e) {} | |
1363 | ||
1364 | if (global.document) { | |
1365 | delete Request.requests[this.index]; | |
1366 | } | |
f8e5fb60 | 1367 | |
6ab4b1ba D |
1368 | this.xhr = null; |
1369 | }; | |
d99e7823 | 1370 | |
f8e5fb60 | 1371 | /** |
6ab4b1ba | 1372 | * Aborts the request. |
a74c0f0c | 1373 | * |
6ab4b1ba | 1374 | * @api public |
f8e5fb60 JA |
1375 | */ |
1376 | ||
6ab4b1ba D |
1377 | Request.prototype.abort = function(){ |
1378 | this.cleanup(); | |
1379 | }; | |
a74c0f0c | 1380 | |
6ab4b1ba D |
1381 | /** |
1382 | * Aborts pending requests when unloading the window. This is needed to prevent | |
1383 | * memory leaks (e.g. when using IE) and to ensure that no spurious error is | |
1384 | * emitted. | |
1385 | */ | |
1386 | ||
1387 | if (global.document) { | |
1388 | Request.requestsCount = 0; | |
1389 | Request.requests = {}; | |
1390 | if (global.attachEvent) { | |
1391 | global.attachEvent('onunload', unloadHandler); | |
1392 | } else if (global.addEventListener) { | |
1393 | global.addEventListener('beforeunload', unloadHandler); | |
a74c0f0c | 1394 | } |
6ab4b1ba | 1395 | } |
a74c0f0c | 1396 | |
6ab4b1ba D |
1397 | function unloadHandler() { |
1398 | for (var i in Request.requests) { | |
1399 | if (Request.requests.hasOwnProperty(i)) { | |
1400 | Request.requests[i].abort(); | |
1401 | } | |
a74c0f0c | 1402 | } |
6ab4b1ba | 1403 | } |
a74c0f0c | 1404 | |
6ab4b1ba | 1405 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) |
e65c2242 | 1406 | },{"./polling":8,"component-emitter":12,"component-inherit":13,"debug":14,"xmlhttprequest":10}],8:[function(_dereq_,module,exports){ |
d99e7823 D |
1407 | /** |
1408 | * Module dependencies. | |
1409 | */ | |
1410 | ||
6ab4b1ba D |
1411 | var Transport = _dereq_('../transport'); |
1412 | var parseqs = _dereq_('parseqs'); | |
1413 | var parser = _dereq_('engine.io-parser'); | |
e65c2242 | 1414 | var inherit = _dereq_('component-inherit'); |
6ab4b1ba | 1415 | var debug = _dereq_('debug')('engine.io-client:polling'); |
d99e7823 D |
1416 | |
1417 | /** | |
1418 | * Module exports. | |
1419 | */ | |
1420 | ||
1421 | module.exports = Polling; | |
1422 | ||
1423 | /** | |
6ab4b1ba | 1424 | * Is XHR2 supported? |
d99e7823 D |
1425 | */ |
1426 | ||
6ab4b1ba D |
1427 | var hasXHR2 = (function() { |
1428 | var XMLHttpRequest = _dereq_('xmlhttprequest'); | |
1429 | var xhr = new XMLHttpRequest({ agent: this.agent, xdomain: false }); | |
1430 | return null != xhr.responseType; | |
1431 | })(); | |
d99e7823 D |
1432 | |
1433 | /** | |
1434 | * Polling interface. | |
1435 | * | |
1436 | * @param {Object} opts | |
1437 | * @api private | |
1438 | */ | |
1439 | ||
1440 | function Polling(opts){ | |
6ab4b1ba D |
1441 | var forceBase64 = (opts && opts.forceBase64); |
1442 | if (!hasXHR2 || forceBase64) { | |
1443 | this.supportsBinary = false; | |
1444 | } | |
d99e7823 D |
1445 | Transport.call(this, opts); |
1446 | } | |
1447 | ||
1448 | /** | |
1449 | * Inherits from Transport. | |
1450 | */ | |
1451 | ||
6ab4b1ba | 1452 | inherit(Polling, Transport); |
d99e7823 D |
1453 | |
1454 | /** | |
1455 | * Transport name. | |
1456 | */ | |
1457 | ||
1458 | Polling.prototype.name = 'polling'; | |
1459 | ||
1460 | /** | |
1461 | * Opens the socket (triggers polling). We write a PING message to determine | |
1462 | * when the transport is open. | |
1463 | * | |
1464 | * @api private | |
1465 | */ | |
1466 | ||
1467 | Polling.prototype.doOpen = function(){ | |
1468 | this.poll(); | |
1469 | }; | |
1470 | ||
1471 | /** | |
1472 | * Pauses polling. | |
1473 | * | |
1474 | * @param {Function} callback upon buffers are flushed and transport is paused | |
1475 | * @api private | |
1476 | */ | |
1477 | ||
1478 | Polling.prototype.pause = function(onPause){ | |
1479 | var pending = 0; | |
1480 | var self = this; | |
1481 | ||
1482 | this.readyState = 'pausing'; | |
1483 | ||
1484 | function pause(){ | |
1485 | debug('paused'); | |
1486 | self.readyState = 'paused'; | |
1487 | onPause(); | |
1488 | } | |
1489 | ||
1490 | if (this.polling || !this.writable) { | |
1491 | var total = 0; | |
1492 | ||
1493 | if (this.polling) { | |
1494 | debug('we are currently polling - waiting to pause'); | |
1495 | total++; | |
1496 | this.once('pollComplete', function(){ | |
1497 | debug('pre-pause polling complete'); | |
1498 | --total || pause(); | |
1499 | }); | |
1500 | } | |
1501 | ||
1502 | if (!this.writable) { | |
1503 | debug('we are currently writing - waiting to pause'); | |
1504 | total++; | |
1505 | this.once('drain', function(){ | |
1506 | debug('pre-pause writing complete'); | |
1507 | --total || pause(); | |
1508 | }); | |
1509 | } | |
1510 | } else { | |
1511 | pause(); | |
1512 | } | |
1513 | }; | |
1514 | ||
1515 | /** | |
1516 | * Starts polling cycle. | |
1517 | * | |
1518 | * @api public | |
1519 | */ | |
1520 | ||
1521 | Polling.prototype.poll = function(){ | |
1522 | debug('polling'); | |
1523 | this.polling = true; | |
1524 | this.doPoll(); | |
1525 | this.emit('poll'); | |
1526 | }; | |
1527 | ||
1528 | /** | |
1529 | * Overloads onData to detect payloads. | |
1530 | * | |
1531 | * @api private | |
1532 | */ | |
1533 | ||
1534 | Polling.prototype.onData = function(data){ | |
1535 | var self = this; | |
1536 | debug('polling got data %s', data); | |
6ab4b1ba | 1537 | var callback = function(packet, index, total) { |
d99e7823 D |
1538 | // if its the first message we consider the transport open |
1539 | if ('opening' == self.readyState) { | |
1540 | self.onOpen(); | |
1541 | } | |
1542 | ||
1543 | // if its a close packet, we close the ongoing requests | |
1544 | if ('close' == packet.type) { | |
1545 | self.onClose(); | |
1546 | return false; | |
1547 | } | |
1548 | ||
1549 | // otherwise bypass onData and handle the message | |
1550 | self.onPacket(packet); | |
6ab4b1ba D |
1551 | }; |
1552 | ||
1553 | // decode payload | |
1554 | parser.decodePayload(data, this.socket.binaryType, callback); | |
d99e7823 D |
1555 | |
1556 | // if an event did not trigger closing | |
1557 | if ('closed' != this.readyState) { | |
1558 | // if we got data we're not polling | |
1559 | this.polling = false; | |
1560 | this.emit('pollComplete'); | |
1561 | ||
1562 | if ('open' == this.readyState) { | |
1563 | this.poll(); | |
1564 | } else { | |
1565 | debug('ignoring poll - transport state "%s"', this.readyState); | |
1566 | } | |
1567 | } | |
1568 | }; | |
1569 | ||
1570 | /** | |
1571 | * For polling, send a close packet. | |
1572 | * | |
1573 | * @api private | |
1574 | */ | |
1575 | ||
1576 | Polling.prototype.doClose = function(){ | |
1577 | var self = this; | |
1578 | ||
1579 | function close(){ | |
1580 | debug('writing close packet'); | |
1581 | self.write([{ type: 'close' }]); | |
1582 | } | |
1583 | ||
6ab4b1ba | 1584 | if ('open' == this.readyState) { |
d99e7823 D |
1585 | debug('transport open - closing'); |
1586 | close(); | |
1587 | } else { | |
1588 | // in case we're trying to close while | |
1589 | // handshaking is in progress (GH-164) | |
6ab4b1ba | 1590 | debug('transport not open - deferring close'); |
d99e7823 D |
1591 | this.once('open', close); |
1592 | } | |
1593 | }; | |
1594 | ||
1595 | /** | |
1596 | * Writes a packets payload. | |
1597 | * | |
1598 | * @param {Array} data packets | |
1599 | * @param {Function} drain callback | |
1600 | * @api private | |
1601 | */ | |
1602 | ||
1603 | Polling.prototype.write = function(packets){ | |
1604 | var self = this; | |
1605 | this.writable = false; | |
6ab4b1ba | 1606 | var callbackfn = function() { |
d99e7823 D |
1607 | self.writable = true; |
1608 | self.emit('drain'); | |
6ab4b1ba D |
1609 | }; |
1610 | ||
1611 | var self = this; | |
1612 | parser.encodePayload(packets, this.supportsBinary, function(data) { | |
1613 | self.doWrite(data, callbackfn); | |
d99e7823 D |
1614 | }); |
1615 | }; | |
1616 | ||
1617 | /** | |
1618 | * Generates uri for connection. | |
1619 | * | |
1620 | * @api private | |
1621 | */ | |
1622 | ||
1623 | Polling.prototype.uri = function(){ | |
1624 | var query = this.query || {}; | |
1625 | var schema = this.secure ? 'https' : 'http'; | |
1626 | var port = ''; | |
1627 | ||
6ab4b1ba D |
1628 | // cache busting is forced |
1629 | if (false !== this.timestampRequests) { | |
1630 | query[this.timestampParam] = +new Date + '-' + Transport.timestamps++; | |
1631 | } | |
1632 | ||
1633 | if (!this.supportsBinary && !query.sid) { | |
1634 | query.b64 = 1; | |
1635 | } | |
1636 | ||
1637 | query = parseqs.encode(query); | |
1638 | ||
1639 | // avoid port if default for schema | |
1640 | if (this.port && (('https' == schema && this.port != 443) || | |
1641 | ('http' == schema && this.port != 80))) { | |
1642 | port = ':' + this.port; | |
1643 | } | |
1644 | ||
1645 | // prepend ? to query | |
1646 | if (query.length) { | |
1647 | query = '?' + query; | |
1648 | } | |
1649 | ||
1650 | return schema + '://' + this.hostname + port + this.path + query; | |
1651 | }; | |
1652 | ||
e65c2242 | 1653 | },{"../transport":4,"component-inherit":13,"debug":14,"engine.io-parser":15,"parseqs":25,"xmlhttprequest":10}],9:[function(_dereq_,module,exports){ |
6ab4b1ba D |
1654 | /** |
1655 | * Module dependencies. | |
1656 | */ | |
1657 | ||
1658 | var Transport = _dereq_('../transport'); | |
1659 | var parser = _dereq_('engine.io-parser'); | |
1660 | var parseqs = _dereq_('parseqs'); | |
e65c2242 | 1661 | var inherit = _dereq_('component-inherit'); |
6ab4b1ba | 1662 | var debug = _dereq_('debug')('engine.io-client:websocket'); |
6ab4b1ba D |
1663 | |
1664 | /** | |
1665 | * `ws` exposes a WebSocket-compatible interface in | |
1666 | * Node, or the `WebSocket` or `MozWebSocket` globals | |
1667 | * in the browser. | |
1668 | */ | |
1669 | ||
1670 | var WebSocket = _dereq_('ws'); | |
1671 | ||
1672 | /** | |
1673 | * Module exports. | |
1674 | */ | |
1675 | ||
1676 | module.exports = WS; | |
1677 | ||
1678 | /** | |
1679 | * WebSocket transport constructor. | |
1680 | * | |
1681 | * @api {Object} connection options | |
1682 | * @api public | |
1683 | */ | |
1684 | ||
1685 | function WS(opts){ | |
1686 | var forceBase64 = (opts && opts.forceBase64); | |
1687 | if (forceBase64) { | |
1688 | this.supportsBinary = false; | |
1689 | } | |
1690 | Transport.call(this, opts); | |
1691 | } | |
1692 | ||
1693 | /** | |
1694 | * Inherits from Transport. | |
1695 | */ | |
1696 | ||
1697 | inherit(WS, Transport); | |
1698 | ||
1699 | /** | |
1700 | * Transport name. | |
1701 | * | |
1702 | * @api public | |
1703 | */ | |
1704 | ||
1705 | WS.prototype.name = 'websocket'; | |
1706 | ||
1707 | /* | |
1708 | * WebSockets support binary | |
1709 | */ | |
1710 | ||
1711 | WS.prototype.supportsBinary = true; | |
1712 | ||
1713 | /** | |
1714 | * Opens socket. | |
1715 | * | |
1716 | * @api private | |
1717 | */ | |
1718 | ||
1719 | WS.prototype.doOpen = function(){ | |
1720 | if (!this.check()) { | |
1721 | // let probe timeout | |
1722 | return; | |
1723 | } | |
1724 | ||
1725 | var self = this; | |
1726 | var uri = this.uri(); | |
1727 | var protocols = void(0); | |
1728 | var opts = { agent: this.agent }; | |
1729 | ||
1730 | this.ws = new WebSocket(uri, protocols, opts); | |
1731 | ||
1732 | if (this.ws.binaryType === undefined) { | |
1733 | this.supportsBinary = false; | |
1734 | } | |
1735 | ||
1736 | this.ws.binaryType = 'arraybuffer'; | |
1737 | this.addEventListeners(); | |
1738 | }; | |
1739 | ||
1740 | /** | |
1741 | * Adds event listeners to the socket | |
1742 | * | |
1743 | * @api private | |
1744 | */ | |
1745 | ||
1746 | WS.prototype.addEventListeners = function(){ | |
1747 | var self = this; | |
1748 | ||
1749 | this.ws.onopen = function(){ | |
1750 | self.onOpen(); | |
1751 | }; | |
1752 | this.ws.onclose = function(){ | |
1753 | self.onClose(); | |
1754 | }; | |
1755 | this.ws.onmessage = function(ev){ | |
1756 | self.onData(ev.data); | |
1757 | }; | |
1758 | this.ws.onerror = function(e){ | |
1759 | self.onError('websocket error', e); | |
1760 | }; | |
1761 | }; | |
1762 | ||
1763 | /** | |
1764 | * Override `onData` to use a timer on iOS. | |
1765 | * See: https://gist.github.com/mloughran/2052006 | |
1766 | * | |
1767 | * @api private | |
1768 | */ | |
1769 | ||
1770 | if ('undefined' != typeof navigator | |
1771 | && /iPad|iPhone|iPod/i.test(navigator.userAgent)) { | |
1772 | WS.prototype.onData = function(data){ | |
1773 | var self = this; | |
1774 | setTimeout(function(){ | |
1775 | Transport.prototype.onData.call(self, data); | |
1776 | }, 0); | |
1777 | }; | |
1778 | } | |
1779 | ||
1780 | /** | |
1781 | * Writes data to socket. | |
1782 | * | |
1783 | * @param {Array} array of packets. | |
1784 | * @api private | |
1785 | */ | |
1786 | ||
1787 | WS.prototype.write = function(packets){ | |
1788 | var self = this; | |
1789 | this.writable = false; | |
1790 | // encodePacket efficient as it uses WS framing | |
1791 | // no need for encodePayload | |
1792 | for (var i = 0, l = packets.length; i < l; i++) { | |
1793 | parser.encodePacket(packets[i], this.supportsBinary, function(data) { | |
1794 | //Sometimes the websocket has already been closed but the browser didn't | |
1795 | //have a chance of informing us about it yet, in that case send will | |
1796 | //throw an error | |
1797 | try { | |
1798 | self.ws.send(data); | |
1799 | } catch (e){ | |
1800 | debug('websocket closed before onclose event'); | |
1801 | } | |
1802 | }); | |
1803 | } | |
1804 | ||
1805 | function ondrain() { | |
1806 | self.writable = true; | |
1807 | self.emit('drain'); | |
1808 | } | |
1809 | // fake drain | |
1810 | // defer to next tick to allow Socket to clear writeBuffer | |
1811 | setTimeout(ondrain, 0); | |
1812 | }; | |
1813 | ||
1814 | /** | |
1815 | * Called upon close | |
1816 | * | |
1817 | * @api private | |
1818 | */ | |
1819 | ||
1820 | WS.prototype.onClose = function(){ | |
1821 | Transport.prototype.onClose.call(this); | |
1822 | }; | |
1823 | ||
1824 | /** | |
1825 | * Closes socket. | |
1826 | * | |
1827 | * @api private | |
1828 | */ | |
1829 | ||
1830 | WS.prototype.doClose = function(){ | |
1831 | if (typeof this.ws !== 'undefined') { | |
1832 | this.ws.close(); | |
1833 | } | |
1834 | }; | |
1835 | ||
1836 | /** | |
1837 | * Generates uri for connection. | |
1838 | * | |
1839 | * @api private | |
1840 | */ | |
1841 | ||
1842 | WS.prototype.uri = function(){ | |
1843 | var query = this.query || {}; | |
1844 | var schema = this.secure ? 'wss' : 'ws'; | |
1845 | var port = ''; | |
1846 | ||
1847 | // avoid port if default for schema | |
1848 | if (this.port && (('wss' == schema && this.port != 443) | |
1849 | || ('ws' == schema && this.port != 80))) { | |
1850 | port = ':' + this.port; | |
1851 | } | |
1852 | ||
1853 | // append timestamp to URI | |
1854 | if (this.timestampRequests) { | |
a74c0f0c | 1855 | query[this.timestampParam] = +new Date; |
d99e7823 D |
1856 | } |
1857 | ||
6ab4b1ba D |
1858 | // communicate binary support capabilities |
1859 | if (!this.supportsBinary) { | |
1860 | query.b64 = 1; | |
1861 | } | |
1862 | ||
1863 | query = parseqs.encode(query); | |
1864 | ||
1865 | // prepend ? to query | |
1866 | if (query.length) { | |
1867 | query = '?' + query; | |
1868 | } | |
1869 | ||
1870 | return schema + '://' + this.hostname + port + this.path + query; | |
1871 | }; | |
1872 | ||
1873 | /** | |
1874 | * Feature detection for WebSocket. | |
1875 | * | |
1876 | * @return {Boolean} whether this transport is available. | |
1877 | * @api public | |
1878 | */ | |
1879 | ||
1880 | WS.prototype.check = function(){ | |
1881 | return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name); | |
1882 | }; | |
1883 | ||
e65c2242 | 1884 | },{"../transport":4,"component-inherit":13,"debug":14,"engine.io-parser":15,"parseqs":25,"ws":27}],10:[function(_dereq_,module,exports){ |
6ab4b1ba D |
1885 | // browser shim for xmlhttprequest module |
1886 | var hasCORS = _dereq_('has-cors'); | |
1887 | ||
1888 | module.exports = function(opts) { | |
1889 | var xdomain = opts.xdomain; | |
1890 | ||
1891 | // XMLHttpRequest can be disabled on IE | |
1892 | try { | |
1893 | if ('undefined' != typeof XMLHttpRequest && (!xdomain || hasCORS)) { | |
1894 | return new XMLHttpRequest(); | |
1895 | } | |
1896 | } catch (e) { } | |
1897 | ||
1898 | if (!xdomain) { | |
1899 | try { | |
1900 | return new ActiveXObject('Microsoft.XMLHTTP'); | |
1901 | } catch(e) { } | |
1902 | } | |
1903 | } | |
1904 | ||
1905 | },{"has-cors":21}],11:[function(_dereq_,module,exports){ | |
1906 | (function (global){ | |
1907 | /** | |
1908 | * Create a blob builder even when vendor prefixes exist | |
1909 | */ | |
1910 | ||
1911 | var BlobBuilder = global.BlobBuilder | |
1912 | || global.WebKitBlobBuilder | |
1913 | || global.MSBlobBuilder | |
1914 | || global.MozBlobBuilder; | |
1915 | ||
1916 | /** | |
1917 | * Check if Blob constructor is supported | |
1918 | */ | |
1919 | ||
1920 | var blobSupported = (function() { | |
1921 | try { | |
1922 | var b = new Blob(['hi']); | |
1923 | return b.size == 2; | |
1924 | } catch(e) { | |
1925 | return false; | |
1926 | } | |
1927 | })(); | |
1928 | ||
1929 | /** | |
1930 | * Check if BlobBuilder is supported | |
1931 | */ | |
d99e7823 | 1932 | |
6ab4b1ba D |
1933 | var blobBuilderSupported = BlobBuilder |
1934 | && BlobBuilder.prototype.append | |
1935 | && BlobBuilder.prototype.getBlob; | |
d99e7823 | 1936 | |
6ab4b1ba D |
1937 | function BlobBuilderConstructor(ary, options) { |
1938 | options = options || {}; | |
d99e7823 | 1939 | |
6ab4b1ba D |
1940 | var bb = new BlobBuilder(); |
1941 | for (var i = 0; i < ary.length; i++) { | |
1942 | bb.append(ary[i]); | |
1943 | } | |
1944 | return (options.type) ? bb.getBlob(options.type) : bb.getBlob(); | |
d99e7823 D |
1945 | }; |
1946 | ||
6ab4b1ba D |
1947 | module.exports = (function() { |
1948 | if (blobSupported) { | |
1949 | return global.Blob; | |
1950 | } else if (blobBuilderSupported) { | |
1951 | return BlobBuilderConstructor; | |
1952 | } else { | |
1953 | return undefined; | |
1954 | } | |
1955 | })(); | |
d99e7823 | 1956 | |
6ab4b1ba D |
1957 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) |
1958 | },{}],12:[function(_dereq_,module,exports){ | |
d99e7823 | 1959 | |
6ab4b1ba D |
1960 | /** |
1961 | * Expose `Emitter`. | |
1962 | */ | |
1963 | ||
1964 | module.exports = Emitter; | |
1965 | ||
1966 | /** | |
1967 | * Initialize a new `Emitter`. | |
f8e5fb60 | 1968 | * |
6ab4b1ba | 1969 | * @api public |
f8e5fb60 JA |
1970 | */ |
1971 | ||
6ab4b1ba D |
1972 | function Emitter(obj) { |
1973 | if (obj) return mixin(obj); | |
d99e7823 D |
1974 | }; |
1975 | ||
1976 | /** | |
6ab4b1ba | 1977 | * Mixin the emitter properties. |
d99e7823 | 1978 | * |
6ab4b1ba D |
1979 | * @param {Object} obj |
1980 | * @return {Object} | |
1981 | * @api private | |
d99e7823 D |
1982 | */ |
1983 | ||
6ab4b1ba D |
1984 | function mixin(obj) { |
1985 | for (var key in Emitter.prototype) { | |
1986 | obj[key] = Emitter.prototype[key]; | |
1987 | } | |
1988 | return obj; | |
a74c0f0c | 1989 | } |
d99e7823 D |
1990 | |
1991 | /** | |
6ab4b1ba D |
1992 | * Listen on the given `event` with `fn`. |
1993 | * | |
1994 | * @param {String} event | |
1995 | * @param {Function} fn | |
1996 | * @return {Emitter} | |
1997 | * @api public | |
d99e7823 D |
1998 | */ |
1999 | ||
e65c2242 JA |
2000 | Emitter.prototype.on = |
2001 | Emitter.prototype.addEventListener = function(event, fn){ | |
6ab4b1ba D |
2002 | this._callbacks = this._callbacks || {}; |
2003 | (this._callbacks[event] = this._callbacks[event] || []) | |
2004 | .push(fn); | |
2005 | return this; | |
2006 | }; | |
d99e7823 D |
2007 | |
2008 | /** | |
6ab4b1ba D |
2009 | * Adds an `event` listener that will be invoked a single |
2010 | * time then automatically removed. | |
d99e7823 | 2011 | * |
6ab4b1ba D |
2012 | * @param {String} event |
2013 | * @param {Function} fn | |
2014 | * @return {Emitter} | |
2015 | * @api public | |
d99e7823 D |
2016 | */ |
2017 | ||
6ab4b1ba | 2018 | Emitter.prototype.once = function(event, fn){ |
a74c0f0c | 2019 | var self = this; |
6ab4b1ba | 2020 | this._callbacks = this._callbacks || {}; |
f8e5fb60 | 2021 | |
6ab4b1ba D |
2022 | function on() { |
2023 | self.off(event, on); | |
2024 | fn.apply(this, arguments); | |
f8e5fb60 | 2025 | } |
d99e7823 | 2026 | |
e65c2242 | 2027 | on.fn = fn; |
6ab4b1ba D |
2028 | this.on(event, on); |
2029 | return this; | |
2030 | }; | |
a74c0f0c | 2031 | |
6ab4b1ba D |
2032 | /** |
2033 | * Remove the given callback for `event` or all | |
2034 | * registered callbacks. | |
2035 | * | |
2036 | * @param {String} event | |
2037 | * @param {Function} fn | |
2038 | * @return {Emitter} | |
2039 | * @api public | |
2040 | */ | |
a74c0f0c | 2041 | |
6ab4b1ba D |
2042 | Emitter.prototype.off = |
2043 | Emitter.prototype.removeListener = | |
e65c2242 JA |
2044 | Emitter.prototype.removeAllListeners = |
2045 | Emitter.prototype.removeEventListener = function(event, fn){ | |
6ab4b1ba D |
2046 | this._callbacks = this._callbacks || {}; |
2047 | ||
2048 | // all | |
2049 | if (0 == arguments.length) { | |
2050 | this._callbacks = {}; | |
2051 | return this; | |
d99e7823 D |
2052 | } |
2053 | ||
6ab4b1ba D |
2054 | // specific event |
2055 | var callbacks = this._callbacks[event]; | |
2056 | if (!callbacks) return this; | |
a74c0f0c | 2057 | |
6ab4b1ba D |
2058 | // remove all handlers |
2059 | if (1 == arguments.length) { | |
2060 | delete this._callbacks[event]; | |
2061 | return this; | |
a74c0f0c | 2062 | } |
6ab4b1ba D |
2063 | |
2064 | // remove specific handler | |
e65c2242 JA |
2065 | var cb; |
2066 | for (var i = 0; i < callbacks.length; i++) { | |
2067 | cb = callbacks[i]; | |
2068 | if (cb === fn || cb.fn === fn) { | |
2069 | callbacks.splice(i, 1); | |
2070 | break; | |
2071 | } | |
2072 | } | |
6ab4b1ba | 2073 | return this; |
d99e7823 D |
2074 | }; |
2075 | ||
2076 | /** | |
6ab4b1ba | 2077 | * Emit `event` with the given args. |
d99e7823 | 2078 | * |
6ab4b1ba D |
2079 | * @param {String} event |
2080 | * @param {Mixed} ... | |
2081 | * @return {Emitter} | |
d99e7823 D |
2082 | */ |
2083 | ||
6ab4b1ba D |
2084 | Emitter.prototype.emit = function(event){ |
2085 | this._callbacks = this._callbacks || {}; | |
2086 | var args = [].slice.call(arguments, 1) | |
2087 | , callbacks = this._callbacks[event]; | |
2088 | ||
2089 | if (callbacks) { | |
2090 | callbacks = callbacks.slice(0); | |
2091 | for (var i = 0, len = callbacks.length; i < len; ++i) { | |
2092 | callbacks[i].apply(this, args); | |
2093 | } | |
2094 | } | |
2095 | ||
2096 | return this; | |
d99e7823 D |
2097 | }; |
2098 | ||
f8e5fb60 | 2099 | /** |
6ab4b1ba | 2100 | * Return array of callbacks for `event`. |
a74c0f0c | 2101 | * |
6ab4b1ba D |
2102 | * @param {String} event |
2103 | * @return {Array} | |
2104 | * @api public | |
f8e5fb60 JA |
2105 | */ |
2106 | ||
6ab4b1ba D |
2107 | Emitter.prototype.listeners = function(event){ |
2108 | this._callbacks = this._callbacks || {}; | |
2109 | return this._callbacks[event] || []; | |
a74c0f0c | 2110 | }; |
d99e7823 D |
2111 | |
2112 | /** | |
6ab4b1ba | 2113 | * Check if this emitter has `event` handlers. |
f8e5fb60 | 2114 | * |
6ab4b1ba D |
2115 | * @param {String} event |
2116 | * @return {Boolean} | |
2117 | * @api public | |
d99e7823 D |
2118 | */ |
2119 | ||
6ab4b1ba D |
2120 | Emitter.prototype.hasListeners = function(event){ |
2121 | return !! this.listeners(event).length; | |
f8e5fb60 | 2122 | }; |
d99e7823 | 2123 | |
e65c2242 JA |
2124 | },{}],13:[function(_dereq_,module,exports){ |
2125 | ||
2126 | module.exports = function(a, b){ | |
2127 | var fn = function(){}; | |
2128 | fn.prototype = b.prototype; | |
2129 | a.prototype = new fn; | |
2130 | a.prototype.constructor = a; | |
2131 | }; | |
2132 | },{}],14:[function(_dereq_,module,exports){ | |
2133 | ||
2134 | /** | |
2135 | * Expose `debug()` as the module. | |
2136 | */ | |
2137 | ||
2138 | module.exports = debug; | |
2139 | ||
2140 | /** | |
2141 | * Create a debugger with the given `name`. | |
2142 | * | |
2143 | * @param {String} name | |
2144 | * @return {Type} | |
2145 | * @api public | |
2146 | */ | |
2147 | ||
2148 | function debug(name) { | |
2149 | if (!debug.enabled(name)) return function(){}; | |
2150 | ||
2151 | return function(fmt){ | |
2152 | fmt = coerce(fmt); | |
2153 | ||
2154 | var curr = new Date; | |
2155 | var ms = curr - (debug[name] || curr); | |
2156 | debug[name] = curr; | |
2157 | ||
2158 | fmt = name | |
2159 | + ' ' | |
2160 | + fmt | |
2161 | + ' +' + debug.humanize(ms); | |
2162 | ||
2163 | // This hackery is required for IE8 | |
2164 | // where `console.log` doesn't have 'apply' | |
2165 | window.console | |
2166 | && console.log | |
2167 | && Function.prototype.apply.call(console.log, console, arguments); | |
2168 | } | |
2169 | } | |
2170 | ||
2171 | /** | |
2172 | * The currently active debug mode names. | |
2173 | */ | |
2174 | ||
2175 | debug.names = []; | |
2176 | debug.skips = []; | |
2177 | ||
2178 | /** | |
2179 | * Enables a debug mode by name. This can include modes | |
2180 | * separated by a colon and wildcards. | |
2181 | * | |
2182 | * @param {String} name | |
2183 | * @api public | |
2184 | */ | |
2185 | ||
2186 | debug.enable = function(name) { | |
2187 | try { | |
2188 | localStorage.debug = name; | |
2189 | } catch(e){} | |
2190 | ||
2191 | var split = (name || '').split(/[\s,]+/) | |
2192 | , len = split.length; | |
2193 | ||
2194 | for (var i = 0; i < len; i++) { | |
2195 | name = split[i].replace('*', '.*?'); | |
2196 | if (name[0] === '-') { | |
2197 | debug.skips.push(new RegExp('^' + name.substr(1) + '$')); | |
2198 | } | |
2199 | else { | |
2200 | debug.names.push(new RegExp('^' + name + '$')); | |
2201 | } | |
2202 | } | |
2203 | }; | |
2204 | ||
2205 | /** | |
2206 | * Disable debug output. | |
2207 | * | |
2208 | * @api public | |
2209 | */ | |
2210 | ||
2211 | debug.disable = function(){ | |
2212 | debug.enable(''); | |
2213 | }; | |
2214 | ||
2215 | /** | |
2216 | * Humanize the given `ms`. | |
2217 | * | |
2218 | * @param {Number} m | |
2219 | * @return {String} | |
2220 | * @api private | |
2221 | */ | |
2222 | ||
2223 | debug.humanize = function(ms) { | |
2224 | var sec = 1000 | |
2225 | , min = 60 * 1000 | |
2226 | , hour = 60 * min; | |
2227 | ||
2228 | if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; | |
2229 | if (ms >= min) return (ms / min).toFixed(1) + 'm'; | |
2230 | if (ms >= sec) return (ms / sec | 0) + 's'; | |
2231 | return ms + 'ms'; | |
2232 | }; | |
2233 | ||
2234 | /** | |
2235 | * Returns true if the given mode name is enabled, false otherwise. | |
2236 | * | |
2237 | * @param {String} name | |
2238 | * @return {Boolean} | |
2239 | * @api public | |
2240 | */ | |
2241 | ||
2242 | debug.enabled = function(name) { | |
2243 | for (var i = 0, len = debug.skips.length; i < len; i++) { | |
2244 | if (debug.skips[i].test(name)) { | |
2245 | return false; | |
2246 | } | |
2247 | } | |
2248 | for (var i = 0, len = debug.names.length; i < len; i++) { | |
2249 | if (debug.names[i].test(name)) { | |
2250 | return true; | |
2251 | } | |
2252 | } | |
2253 | return false; | |
2254 | }; | |
2255 | ||
2256 | /** | |
2257 | * Coerce `val`. | |
2258 | */ | |
2259 | ||
2260 | function coerce(val) { | |
2261 | if (val instanceof Error) return val.stack || val.message; | |
2262 | return val; | |
2263 | } | |
2264 | ||
2265 | // persist | |
2266 | ||
2267 | try { | |
2268 | if (window.localStorage) debug.enable(localStorage.debug); | |
2269 | } catch(e){} | |
2270 | ||
2271 | },{}],15:[function(_dereq_,module,exports){ | |
6ab4b1ba | 2272 | (function (global){ |
d99e7823 | 2273 | /** |
6ab4b1ba | 2274 | * Module dependencies. |
d99e7823 D |
2275 | */ |
2276 | ||
6ab4b1ba D |
2277 | var keys = _dereq_('./keys'); |
2278 | var sliceBuffer = _dereq_('arraybuffer.slice'); | |
2279 | var base64encoder = _dereq_('base64-arraybuffer'); | |
2280 | var after = _dereq_('after'); | |
2281 | var utf8 = _dereq_('utf8'); | |
f8e5fb60 | 2282 | |
6ab4b1ba D |
2283 | /** |
2284 | * Check if we are running an android browser. That requires us to use | |
2285 | * ArrayBuffer with polling transports... | |
2286 | * | |
2287 | * http://ghinda.net/jpeg-blob-ajax-android/ | |
2288 | */ | |
d99e7823 | 2289 | |
6ab4b1ba | 2290 | var isAndroid = navigator.userAgent.match(/Android/i); |
d99e7823 | 2291 | |
6ab4b1ba D |
2292 | /** |
2293 | * Current protocol version. | |
2294 | */ | |
a74c0f0c | 2295 | |
6ab4b1ba | 2296 | exports.protocol = 2; |
d99e7823 D |
2297 | |
2298 | /** | |
6ab4b1ba | 2299 | * Packet types. |
d99e7823 D |
2300 | */ |
2301 | ||
6ab4b1ba D |
2302 | var packets = exports.packets = { |
2303 | open: 0 // non-ws | |
2304 | , close: 1 // non-ws | |
2305 | , ping: 2 | |
2306 | , pong: 3 | |
2307 | , message: 4 | |
2308 | , upgrade: 5 | |
2309 | , noop: 6 | |
f8e5fb60 | 2310 | }; |
d99e7823 | 2311 | |
6ab4b1ba | 2312 | var packetslist = keys(packets); |
a74c0f0c | 2313 | |
d99e7823 | 2314 | /** |
6ab4b1ba | 2315 | * Premade error packet. |
d99e7823 D |
2316 | */ |
2317 | ||
6ab4b1ba | 2318 | var err = { type: 'error', data: 'parser error' }; |
f8e5fb60 | 2319 | |
a74c0f0c | 2320 | /** |
6ab4b1ba | 2321 | * Create a blob api even for blob builder when vendor prefixes exist |
a74c0f0c | 2322 | */ |
f8e5fb60 | 2323 | |
6ab4b1ba | 2324 | var Blob = _dereq_('blob'); |
f8e5fb60 | 2325 | |
a74c0f0c | 2326 | /** |
6ab4b1ba D |
2327 | * Encodes a packet. |
2328 | * | |
2329 | * <packet type id> [ <data> ] | |
2330 | * | |
2331 | * Example: | |
2332 | * | |
2333 | * 5hello world | |
2334 | * 3 | |
2335 | * 4 | |
2336 | * | |
2337 | * Binary is encoded in an identical principle | |
2338 | * | |
2339 | * @api private | |
a74c0f0c | 2340 | */ |
f8e5fb60 | 2341 | |
6ab4b1ba D |
2342 | exports.encodePacket = function (packet, supportsBinary, callback) { |
2343 | if (typeof supportsBinary == 'function') { | |
2344 | callback = supportsBinary; | |
2345 | supportsBinary = false; | |
2346 | } | |
d99e7823 | 2347 | |
6ab4b1ba D |
2348 | var data = (packet.data === undefined) |
2349 | ? undefined | |
2350 | : packet.data.buffer || packet.data; | |
d99e7823 | 2351 | |
6ab4b1ba D |
2352 | if (global.ArrayBuffer && data instanceof ArrayBuffer) { |
2353 | return encodeArrayBuffer(packet, supportsBinary, callback); | |
2354 | } else if (Blob && data instanceof global.Blob) { | |
2355 | return encodeBlob(packet, supportsBinary, callback); | |
2356 | } | |
d99e7823 | 2357 | |
6ab4b1ba D |
2358 | // Sending data as a utf-8 string |
2359 | var encoded = packets[packet.type]; | |
d99e7823 | 2360 | |
6ab4b1ba D |
2361 | // data fragment is optional |
2362 | if (undefined !== packet.data) { | |
2363 | encoded += utf8.encode(String(packet.data)); | |
2364 | } | |
d99e7823 | 2365 | |
6ab4b1ba | 2366 | return callback('' + encoded); |
d99e7823 | 2367 | |
6ab4b1ba | 2368 | }; |
d99e7823 | 2369 | |
f8e5fb60 | 2370 | /** |
6ab4b1ba | 2371 | * Encode packet helpers for binary types |
f8e5fb60 JA |
2372 | */ |
2373 | ||
6ab4b1ba D |
2374 | function encodeArrayBuffer(packet, supportsBinary, callback) { |
2375 | if (!supportsBinary) { | |
2376 | return exports.encodeBase64Packet(packet, callback); | |
2377 | } | |
f8e5fb60 | 2378 | |
6ab4b1ba D |
2379 | var data = packet.data; |
2380 | var contentArray = new Uint8Array(data); | |
2381 | var resultBuffer = new Uint8Array(1 + data.byteLength); | |
d99e7823 | 2382 | |
6ab4b1ba D |
2383 | resultBuffer[0] = packets[packet.type]; |
2384 | for (var i = 0; i < contentArray.length; i++) { | |
2385 | resultBuffer[i+1] = contentArray[i]; | |
2386 | } | |
a74c0f0c | 2387 | |
6ab4b1ba D |
2388 | return callback(resultBuffer.buffer); |
2389 | } | |
2390 | ||
2391 | function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) { | |
2392 | if (!supportsBinary) { | |
2393 | return exports.encodeBase64Packet(packet, callback); | |
a74c0f0c D |
2394 | } |
2395 | ||
6ab4b1ba D |
2396 | var fr = new FileReader(); |
2397 | fr.onload = function() { | |
2398 | packet.data = fr.result; | |
2399 | exports.encodePacket(packet, supportsBinary, callback); | |
2400 | }; | |
2401 | return fr.readAsArrayBuffer(packet.data); | |
2402 | } | |
a74c0f0c | 2403 | |
6ab4b1ba D |
2404 | function encodeBlob(packet, supportsBinary, callback) { |
2405 | if (!supportsBinary) { | |
2406 | return exports.encodeBase64Packet(packet, callback); | |
2407 | } | |
a74c0f0c | 2408 | |
6ab4b1ba D |
2409 | if (isAndroid) { |
2410 | return encodeBlobAsArrayBuffer(packet, supportsBinary, callback); | |
2411 | } | |
2412 | ||
2413 | var length = new Uint8Array(1); | |
2414 | length[0] = packets[packet.type]; | |
2415 | var blob = new Blob([length.buffer, packet.data]); | |
2416 | ||
2417 | return callback(blob); | |
2418 | } | |
d99e7823 D |
2419 | |
2420 | /** | |
6ab4b1ba D |
2421 | * Encodes a packet with binary data in a base64 string |
2422 | * | |
2423 | * @param {Object} packet, has `type` and `data` | |
2424 | * @return {String} base64 encoded message | |
d99e7823 D |
2425 | */ |
2426 | ||
6ab4b1ba D |
2427 | exports.encodeBase64Packet = function(packet, callback) { |
2428 | var message = 'b' + exports.packets[packet.type]; | |
2429 | if (Blob && packet.data instanceof Blob) { | |
2430 | var fr = new FileReader(); | |
2431 | fr.onload = function() { | |
2432 | var b64 = fr.result.split(',')[1]; | |
2433 | callback(message + b64); | |
2434 | }; | |
2435 | return fr.readAsDataURL(packet.data); | |
2436 | } | |
2437 | ||
2438 | var b64data; | |
2439 | try { | |
2440 | b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data)); | |
2441 | } catch (e) { | |
2442 | // iPhone Safari doesn't let you apply with typed arrays | |
2443 | var typed = new Uint8Array(packet.data); | |
2444 | var basic = new Array(typed.length); | |
2445 | for (var i = 0; i < typed.length; i++) { | |
2446 | basic[i] = typed[i]; | |
2447 | } | |
2448 | b64data = String.fromCharCode.apply(null, basic); | |
2449 | } | |
2450 | message += global.btoa(b64data); | |
2451 | return callback(message); | |
2452 | }; | |
d99e7823 D |
2453 | |
2454 | /** | |
6ab4b1ba | 2455 | * Decodes a packet. Changes format to Blob if requested. |
d99e7823 | 2456 | * |
6ab4b1ba | 2457 | * @return {Object} with `type` and `data` (if any) |
d99e7823 D |
2458 | * @api private |
2459 | */ | |
2460 | ||
6ab4b1ba D |
2461 | exports.decodePacket = function (data, binaryType) { |
2462 | // String data | |
2463 | if (typeof data == 'string' || data === undefined) { | |
2464 | if (data.charAt(0) == 'b') { | |
2465 | return exports.decodeBase64Packet(data.substr(1), binaryType); | |
2466 | } | |
2467 | ||
2468 | data = utf8.decode(data); | |
2469 | var type = data.charAt(0); | |
2470 | ||
2471 | if (Number(type) != type || !packetslist[type]) { | |
2472 | return err; | |
2473 | } | |
2474 | ||
2475 | if (data.length > 1) { | |
2476 | return { type: packetslist[type], data: data.substring(1) }; | |
2477 | } else { | |
2478 | return { type: packetslist[type] }; | |
2479 | } | |
2480 | } | |
2481 | ||
2482 | var asArray = new Uint8Array(data); | |
2483 | var type = asArray[0]; | |
2484 | var rest = sliceBuffer(data, 1); | |
2485 | if (Blob && binaryType === 'blob') { | |
2486 | rest = new Blob([rest]); | |
2487 | } | |
2488 | return { type: packetslist[type], data: rest }; | |
d99e7823 D |
2489 | }; |
2490 | ||
2491 | /** | |
6ab4b1ba | 2492 | * Decodes a packet encoded in a base64 string |
d99e7823 | 2493 | * |
6ab4b1ba D |
2494 | * @param {String} base64 encoded message |
2495 | * @return {Object} with `type` and `data` (if any) | |
d99e7823 D |
2496 | */ |
2497 | ||
6ab4b1ba D |
2498 | exports.decodeBase64Packet = function(msg, binaryType) { |
2499 | var type = packetslist[msg.charAt(0)]; | |
2500 | if (!global.ArrayBuffer) { | |
2501 | return { type: type, data: { base64: true, data: msg.substr(1) } }; | |
a74c0f0c | 2502 | } |
d99e7823 | 2503 | |
6ab4b1ba D |
2504 | var data = base64encoder.decode(msg.substr(1)); |
2505 | ||
2506 | if (binaryType === 'blob' && Blob) { | |
2507 | data = new Blob([data]); | |
d99e7823 D |
2508 | } |
2509 | ||
6ab4b1ba | 2510 | return { type: type, data: data }; |
d99e7823 D |
2511 | }; |
2512 | ||
2513 | /** | |
6ab4b1ba D |
2514 | * Encodes multiple messages (payload). |
2515 | * | |
2516 | * <length>:data | |
2517 | * | |
2518 | * Example: | |
2519 | * | |
2520 | * 11:hello world2:hi | |
2521 | * | |
2522 | * If any contents are binary, they will be encoded as base64 strings. Base64 | |
2523 | * encoded strings are marked with a b before the length specifier | |
d99e7823 | 2524 | * |
6ab4b1ba | 2525 | * @param {Array} packets |
d99e7823 D |
2526 | * @api private |
2527 | */ | |
2528 | ||
6ab4b1ba D |
2529 | exports.encodePayload = function (packets, supportsBinary, callback) { |
2530 | if (typeof supportsBinary == 'function') { | |
2531 | callback = supportsBinary; | |
2532 | supportsBinary = null; | |
2533 | } | |
a74c0f0c | 2534 | |
6ab4b1ba D |
2535 | if (supportsBinary) { |
2536 | if (Blob && !isAndroid) { | |
2537 | return exports.encodePayloadAsBlob(packets, callback); | |
2538 | } | |
2539 | ||
2540 | return exports.encodePayloadAsArrayBuffer(packets, callback); | |
d99e7823 D |
2541 | } |
2542 | ||
6ab4b1ba D |
2543 | if (!packets.length) { |
2544 | return callback('0:'); | |
2545 | } | |
2546 | ||
2547 | function setLengthHeader(message) { | |
2548 | return message.length + ':' + message; | |
2549 | } | |
2550 | ||
2551 | function encodeOne(packet, doneCallback) { | |
2552 | exports.encodePacket(packet, supportsBinary, function(message) { | |
2553 | doneCallback(null, setLengthHeader(message)); | |
2554 | }); | |
2555 | } | |
2556 | ||
2557 | map(packets, encodeOne, function(err, results) { | |
2558 | return callback(results.join('')); | |
2559 | }); | |
2560 | }; | |
2561 | ||
2562 | /** | |
2563 | * Async array map using after | |
2564 | */ | |
d99e7823 | 2565 | |
6ab4b1ba D |
2566 | function map(ary, each, done) { |
2567 | var result = new Array(ary.length); | |
2568 | var next = after(ary.length, done); | |
d99e7823 | 2569 | |
6ab4b1ba D |
2570 | var eachWithIndex = function(i, el, cb) { |
2571 | each(el, function(error, msg) { | |
2572 | result[i] = msg; | |
2573 | cb(error, result); | |
2574 | }); | |
2575 | }; | |
d99e7823 | 2576 | |
6ab4b1ba D |
2577 | for (var i = 0; i < ary.length; i++) { |
2578 | eachWithIndex(i, ary[i], next); | |
d99e7823 | 2579 | } |
6ab4b1ba | 2580 | } |
f8e5fb60 | 2581 | |
6ab4b1ba D |
2582 | /* |
2583 | * Decodes data when a payload is maybe expected. Possible binary contents are | |
2584 | * decoded from their base64 representation | |
d99e7823 | 2585 | * |
6ab4b1ba D |
2586 | * @param {String} data, callback method |
2587 | * @api public | |
d99e7823 D |
2588 | */ |
2589 | ||
6ab4b1ba D |
2590 | exports.decodePayload = function (data, binaryType, callback) { |
2591 | if (typeof data != 'string') { | |
2592 | return exports.decodePayloadAsBinary(data, binaryType, callback); | |
2593 | } | |
d99e7823 | 2594 | |
6ab4b1ba D |
2595 | if (typeof binaryType === 'function') { |
2596 | callback = binaryType; | |
2597 | binaryType = null; | |
2598 | } | |
f8e5fb60 | 2599 | |
6ab4b1ba D |
2600 | var packet; |
2601 | if (data == '') { | |
2602 | // parser error - ignoring payload | |
2603 | return callback(err, 0, 1); | |
d99e7823 | 2604 | } |
d99e7823 | 2605 | |
6ab4b1ba D |
2606 | var length = '' |
2607 | , n, msg; | |
d99e7823 | 2608 | |
6ab4b1ba D |
2609 | for (var i = 0, l = data.length; i < l; i++) { |
2610 | var chr = data.charAt(i); | |
d99e7823 | 2611 | |
6ab4b1ba D |
2612 | if (':' != chr) { |
2613 | length += chr; | |
2614 | } else { | |
2615 | if ('' == length || (length != (n = Number(length)))) { | |
2616 | // parser error - ignoring payload | |
2617 | return callback(err, 0, 1); | |
a74c0f0c | 2618 | } |
d99e7823 | 2619 | |
6ab4b1ba | 2620 | msg = data.substr(i + 1, n); |
d99e7823 | 2621 | |
6ab4b1ba D |
2622 | if (length != msg.length) { |
2623 | // parser error - ignoring payload | |
2624 | return callback(err, 0, 1); | |
2625 | } | |
d99e7823 | 2626 | |
6ab4b1ba D |
2627 | if (msg.length) { |
2628 | packet = exports.decodePacket(msg, binaryType); | |
d99e7823 | 2629 | |
6ab4b1ba D |
2630 | if (err.type == packet.type && err.data == packet.data) { |
2631 | // parser error in individual packet - ignoring payload | |
2632 | return callback(err, 0, 1); | |
2633 | } | |
f8e5fb60 | 2634 | |
6ab4b1ba D |
2635 | var ret = callback(packet, i + n, l); |
2636 | if (false === ret) return; | |
2637 | } | |
d99e7823 | 2638 | |
6ab4b1ba D |
2639 | // advance cursor |
2640 | i += n; | |
2641 | length = ''; | |
2642 | } | |
2643 | } | |
d99e7823 | 2644 | |
6ab4b1ba D |
2645 | if (length != '') { |
2646 | // parser error - ignoring payload | |
2647 | return callback(err, 0, 1); | |
f8e5fb60 | 2648 | } |
6ab4b1ba | 2649 | |
f8e5fb60 | 2650 | }; |
d99e7823 D |
2651 | |
2652 | /** | |
6ab4b1ba D |
2653 | * Encodes multiple messages (payload) as binary. |
2654 | * | |
2655 | * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number | |
2656 | * 255><data> | |
2657 | * | |
2658 | * Example: | |
2659 | * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers | |
2660 | * | |
2661 | * @param {Array} packets | |
2662 | * @return {ArrayBuffer} encoded payload | |
2663 | * @api private | |
d99e7823 D |
2664 | */ |
2665 | ||
6ab4b1ba D |
2666 | exports.encodePayloadAsArrayBuffer = function(packets, callback) { |
2667 | if (!packets.length) { | |
2668 | return callback(new ArrayBuffer(0)); | |
2669 | } | |
2670 | ||
2671 | function encodeOne(packet, doneCallback) { | |
2672 | exports.encodePacket(packet, true, function(data) { | |
2673 | return doneCallback(null, data); | |
2674 | }); | |
2675 | } | |
f8e5fb60 | 2676 | |
6ab4b1ba D |
2677 | map(packets, encodeOne, function(err, encodedPackets) { |
2678 | var totalLength = encodedPackets.reduce(function(acc, p) { | |
2679 | var len; | |
2680 | if (typeof p === 'string'){ | |
2681 | len = p.length; | |
2682 | } else { | |
2683 | len = p.byteLength; | |
2684 | } | |
2685 | return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2 | |
2686 | }, 0); | |
d99e7823 | 2687 | |
6ab4b1ba | 2688 | var resultArray = new Uint8Array(totalLength); |
a74c0f0c | 2689 | |
6ab4b1ba D |
2690 | var bufferIndex = 0; |
2691 | encodedPackets.forEach(function(p) { | |
2692 | var isString = typeof p === 'string'; | |
2693 | var ab = p; | |
2694 | if (isString) { | |
2695 | var view = new Uint8Array(p.length); | |
2696 | for (var i = 0; i < p.length; i++) { | |
2697 | view[i] = p.charCodeAt(i); | |
2698 | } | |
2699 | ab = view.buffer; | |
2700 | } | |
d99e7823 | 2701 | |
6ab4b1ba D |
2702 | if (isString) { // not true binary |
2703 | resultArray[bufferIndex++] = 0; | |
2704 | } else { // true binary | |
2705 | resultArray[bufferIndex++] = 1; | |
2706 | } | |
d99e7823 | 2707 | |
6ab4b1ba D |
2708 | var lenStr = ab.byteLength.toString(); |
2709 | for (var i = 0; i < lenStr.length; i++) { | |
2710 | resultArray[bufferIndex++] = parseInt(lenStr[i]); | |
2711 | } | |
2712 | resultArray[bufferIndex++] = 255; | |
d99e7823 | 2713 | |
6ab4b1ba D |
2714 | var view = new Uint8Array(ab); |
2715 | for (var i = 0; i < view.length; i++) { | |
2716 | resultArray[bufferIndex++] = view[i]; | |
2717 | } | |
2718 | }); | |
2719 | ||
2720 | return callback(resultArray.buffer); | |
2721 | }); | |
a74c0f0c | 2722 | }; |
d99e7823 D |
2723 | |
2724 | /** | |
6ab4b1ba | 2725 | * Encode as Blob |
d99e7823 D |
2726 | */ |
2727 | ||
6ab4b1ba D |
2728 | exports.encodePayloadAsBlob = function(packets, callback) { |
2729 | function encodeOne(packet, doneCallback) { | |
2730 | exports.encodePacket(packet, true, function(encoded) { | |
2731 | var binaryIdentifier = new Uint8Array(1); | |
2732 | binaryIdentifier[0] = 1; | |
2733 | if (typeof encoded === 'string') { | |
2734 | var view = new Uint8Array(encoded.length); | |
2735 | for (var i = 0; i < encoded.length; i++) { | |
2736 | view[i] = encoded.charCodeAt(i); | |
2737 | } | |
2738 | encoded = view.buffer; | |
2739 | binaryIdentifier[0] = 0; | |
2740 | } | |
d99e7823 | 2741 | |
6ab4b1ba D |
2742 | var len = (encoded instanceof ArrayBuffer) |
2743 | ? encoded.byteLength | |
2744 | : encoded.size; | |
d99e7823 | 2745 | |
6ab4b1ba D |
2746 | var lenStr = len.toString(); |
2747 | var lengthAry = new Uint8Array(lenStr.length + 1); | |
2748 | for (var i = 0; i < lenStr.length; i++) { | |
2749 | lengthAry[i] = parseInt(lenStr[i]); | |
2750 | } | |
2751 | lengthAry[lenStr.length] = 255; | |
d99e7823 | 2752 | |
6ab4b1ba D |
2753 | if (Blob) { |
2754 | var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]); | |
2755 | doneCallback(null, blob); | |
2756 | } | |
2757 | }); | |
d99e7823 | 2758 | } |
a74c0f0c | 2759 | |
6ab4b1ba D |
2760 | map(packets, encodeOne, function(err, results) { |
2761 | return callback(new Blob(results)); | |
2762 | }); | |
a74c0f0c | 2763 | }; |
d99e7823 | 2764 | |
6ab4b1ba D |
2765 | /* |
2766 | * Decodes data when a payload is maybe expected. Strings are decoded by | |
2767 | * interpreting each byte as a key code for entries marked to start with 0. See | |
2768 | * description of encodePayloadAsBinary | |
d99e7823 | 2769 | * |
6ab4b1ba D |
2770 | * @param {ArrayBuffer} data, callback method |
2771 | * @api public | |
d99e7823 D |
2772 | */ |
2773 | ||
6ab4b1ba D |
2774 | exports.decodePayloadAsBinary = function (data, binaryType, callback) { |
2775 | if (typeof binaryType === 'function') { | |
2776 | callback = binaryType; | |
2777 | binaryType = null; | |
2778 | } | |
d99e7823 | 2779 | |
6ab4b1ba D |
2780 | var bufferTail = data; |
2781 | var buffers = []; | |
d99e7823 | 2782 | |
6ab4b1ba D |
2783 | while (bufferTail.byteLength > 0) { |
2784 | var tailArray = new Uint8Array(bufferTail); | |
2785 | var isString = tailArray[0] === 0; | |
2786 | var msgLength = ''; | |
2787 | for (var i = 1; ; i++) { | |
2788 | if (tailArray[i] == 255) break; | |
2789 | msgLength += tailArray[i]; | |
2790 | } | |
2791 | bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length); | |
2792 | msgLength = parseInt(msgLength); | |
2793 | ||
2794 | var msg = sliceBuffer(bufferTail, 0, msgLength); | |
2795 | if (isString) { | |
2796 | try { | |
2797 | msg = String.fromCharCode.apply(null, new Uint8Array(msg)); | |
2798 | } catch (e) { | |
2799 | // iPhone Safari doesn't let you apply to typed arrays | |
2800 | var typed = new Uint8Array(msg); | |
2801 | msg = ''; | |
2802 | for (var i = 0; i < typed.length; i++) { | |
2803 | msg += String.fromCharCode(typed[i]); | |
2804 | } | |
a74c0f0c | 2805 | } |
6ab4b1ba D |
2806 | } |
2807 | buffers.push(msg); | |
2808 | bufferTail = sliceBuffer(bufferTail, msgLength); | |
d99e7823 | 2809 | } |
6ab4b1ba D |
2810 | |
2811 | var total = buffers.length; | |
2812 | buffers.forEach(function(buffer, i) { | |
2813 | callback(exports.decodePacket(buffer, binaryType), i, total); | |
2814 | }); | |
d99e7823 D |
2815 | }; |
2816 | ||
6ab4b1ba | 2817 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) |
e65c2242 | 2818 | },{"./keys":16,"after":17,"arraybuffer.slice":18,"base64-arraybuffer":19,"blob":11,"utf8":20}],16:[function(_dereq_,module,exports){ |
6ab4b1ba | 2819 | |
d99e7823 | 2820 | /** |
6ab4b1ba | 2821 | * Gets the keys for an object. |
d99e7823 | 2822 | * |
6ab4b1ba | 2823 | * @return {Array} keys |
a74c0f0c | 2824 | * @api private |
d99e7823 D |
2825 | */ |
2826 | ||
6ab4b1ba D |
2827 | module.exports = Object.keys || function keys (obj){ |
2828 | var arr = []; | |
2829 | var has = Object.prototype.hasOwnProperty; | |
2830 | ||
2831 | for (var i in obj) { | |
2832 | if (has.call(obj, i)) { | |
2833 | arr.push(i); | |
2834 | } | |
2835 | } | |
2836 | return arr; | |
a74c0f0c | 2837 | }; |
d99e7823 | 2838 | |
e65c2242 | 2839 | },{}],17:[function(_dereq_,module,exports){ |
6ab4b1ba D |
2840 | module.exports = after |
2841 | ||
2842 | function after(count, callback, err_cb) { | |
2843 | var bail = false | |
2844 | err_cb = err_cb || noop | |
2845 | proxy.count = count | |
2846 | ||
2847 | return (count === 0) ? callback() : proxy | |
2848 | ||
2849 | function proxy(err, result) { | |
2850 | if (proxy.count <= 0) { | |
2851 | throw new Error('after called too many times') | |
2852 | } | |
2853 | --proxy.count | |
2854 | ||
2855 | // after first error, rest are passed to err_cb | |
2856 | if (err) { | |
2857 | bail = true | |
2858 | callback(err) | |
2859 | // future error callbacks will go to error handler | |
2860 | callback = err_cb | |
2861 | } else if (proxy.count === 0 && !bail) { | |
2862 | callback(null, result) | |
2863 | } | |
2864 | } | |
2865 | } | |
2866 | ||
2867 | function noop() {} | |
2868 | ||
e65c2242 | 2869 | },{}],18:[function(_dereq_,module,exports){ |
a74c0f0c | 2870 | /** |
6ab4b1ba D |
2871 | * An abstraction for slicing an arraybuffer even when |
2872 | * ArrayBuffer.prototype.slice is not supported | |
a74c0f0c | 2873 | * |
6ab4b1ba | 2874 | * @api public |
a74c0f0c | 2875 | */ |
f8e5fb60 | 2876 | |
6ab4b1ba D |
2877 | module.exports = function(arraybuffer, start, end) { |
2878 | var bytes = arraybuffer.byteLength; | |
2879 | start = start || 0; | |
2880 | end = end || bytes; | |
2881 | ||
2882 | if (arraybuffer.slice) { return arraybuffer.slice(start, end); } | |
2883 | ||
2884 | if (start < 0) { start += bytes; } | |
2885 | if (end < 0) { end += bytes; } | |
2886 | if (end > bytes) { end = bytes; } | |
2887 | ||
2888 | if (start >= bytes || start >= end || bytes === 0) { | |
2889 | return new ArrayBuffer(0); | |
2890 | } | |
2891 | ||
2892 | var abv = new Uint8Array(arraybuffer); | |
2893 | var result = new Uint8Array(end - start); | |
2894 | for (var i = start, ii = 0; i < end; i++, ii++) { | |
2895 | result[ii] = abv[i]; | |
d99e7823 | 2896 | } |
6ab4b1ba | 2897 | return result.buffer; |
d99e7823 D |
2898 | }; |
2899 | ||
e65c2242 | 2900 | },{}],19:[function(_dereq_,module,exports){ |
6ab4b1ba D |
2901 | /* |
2902 | * base64-arraybuffer | |
2903 | * https://github.com/niklasvh/base64-arraybuffer | |
d99e7823 | 2904 | * |
6ab4b1ba D |
2905 | * Copyright (c) 2012 Niklas von Hertzen |
2906 | * Licensed under the MIT license. | |
d99e7823 | 2907 | */ |
6ab4b1ba D |
2908 | (function(chars){ |
2909 | "use strict"; | |
d99e7823 | 2910 | |
6ab4b1ba D |
2911 | exports.encode = function(arraybuffer) { |
2912 | var bytes = new Uint8Array(arraybuffer), | |
2913 | i, len = bytes.length, base64 = ""; | |
d99e7823 | 2914 | |
6ab4b1ba D |
2915 | for (i = 0; i < len; i+=3) { |
2916 | base64 += chars[bytes[i] >> 2]; | |
2917 | base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; | |
2918 | base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; | |
2919 | base64 += chars[bytes[i + 2] & 63]; | |
2920 | } | |
d99e7823 | 2921 | |
6ab4b1ba D |
2922 | if ((len % 3) === 2) { |
2923 | base64 = base64.substring(0, base64.length - 1) + "="; | |
2924 | } else if (len % 3 === 1) { | |
2925 | base64 = base64.substring(0, base64.length - 2) + "=="; | |
2926 | } | |
a74c0f0c | 2927 | |
6ab4b1ba D |
2928 | return base64; |
2929 | }; | |
a74c0f0c | 2930 | |
6ab4b1ba D |
2931 | exports.decode = function(base64) { |
2932 | var bufferLength = base64.length * 0.75, | |
2933 | len = base64.length, i, p = 0, | |
2934 | encoded1, encoded2, encoded3, encoded4; | |
a74c0f0c | 2935 | |
6ab4b1ba D |
2936 | if (base64[base64.length - 1] === "=") { |
2937 | bufferLength--; | |
2938 | if (base64[base64.length - 2] === "=") { | |
2939 | bufferLength--; | |
2940 | } | |
2941 | } | |
d99e7823 | 2942 | |
6ab4b1ba D |
2943 | var arraybuffer = new ArrayBuffer(bufferLength), |
2944 | bytes = new Uint8Array(arraybuffer); | |
d99e7823 | 2945 | |
6ab4b1ba D |
2946 | for (i = 0; i < len; i+=4) { |
2947 | encoded1 = chars.indexOf(base64[i]); | |
2948 | encoded2 = chars.indexOf(base64[i+1]); | |
2949 | encoded3 = chars.indexOf(base64[i+2]); | |
2950 | encoded4 = chars.indexOf(base64[i+3]); | |
d99e7823 | 2951 | |
6ab4b1ba D |
2952 | bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); |
2953 | bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); | |
2954 | bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); | |
2955 | } | |
d99e7823 | 2956 | |
6ab4b1ba D |
2957 | return arraybuffer; |
2958 | }; | |
2959 | })("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); | |
2960 | ||
e65c2242 | 2961 | },{}],20:[function(_dereq_,module,exports){ |
6ab4b1ba D |
2962 | (function (global){ |
2963 | /*! http://mths.be/utf8js v2.0.0 by @mathias */ | |
2964 | ;(function(root) { | |
2965 | ||
2966 | // Detect free variables `exports` | |
2967 | var freeExports = typeof exports == 'object' && exports; | |
2968 | ||
2969 | // Detect free variable `module` | |
2970 | var freeModule = typeof module == 'object' && module && | |
2971 | module.exports == freeExports && module; | |
2972 | ||
2973 | // Detect free variable `global`, from Node.js or Browserified code, | |
2974 | // and use it as `root` | |
2975 | var freeGlobal = typeof global == 'object' && global; | |
2976 | if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { | |
2977 | root = freeGlobal; | |
2978 | } | |
2979 | ||
2980 | /*--------------------------------------------------------------------------*/ | |
2981 | ||
2982 | var stringFromCharCode = String.fromCharCode; | |
2983 | ||
2984 | // Taken from http://mths.be/punycode | |
2985 | function ucs2decode(string) { | |
2986 | var output = []; | |
2987 | var counter = 0; | |
2988 | var length = string.length; | |
2989 | var value; | |
2990 | var extra; | |
2991 | while (counter < length) { | |
2992 | value = string.charCodeAt(counter++); | |
2993 | if (value >= 0xD800 && value <= 0xDBFF && counter < length) { | |
2994 | // high surrogate, and there is a next character | |
2995 | extra = string.charCodeAt(counter++); | |
2996 | if ((extra & 0xFC00) == 0xDC00) { // low surrogate | |
2997 | output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); | |
2998 | } else { | |
2999 | // unmatched surrogate; only append this code unit, in case the next | |
3000 | // code unit is the high surrogate of a surrogate pair | |
3001 | output.push(value); | |
3002 | counter--; | |
3003 | } | |
3004 | } else { | |
3005 | output.push(value); | |
3006 | } | |
3007 | } | |
3008 | return output; | |
3009 | } | |
3010 | ||
3011 | // Taken from http://mths.be/punycode | |
3012 | function ucs2encode(array) { | |
3013 | var length = array.length; | |
3014 | var index = -1; | |
3015 | var value; | |
3016 | var output = ''; | |
3017 | while (++index < length) { | |
3018 | value = array[index]; | |
3019 | if (value > 0xFFFF) { | |
3020 | value -= 0x10000; | |
3021 | output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); | |
3022 | value = 0xDC00 | value & 0x3FF; | |
3023 | } | |
3024 | output += stringFromCharCode(value); | |
3025 | } | |
3026 | return output; | |
a74c0f0c | 3027 | } |
d99e7823 | 3028 | |
6ab4b1ba | 3029 | /*--------------------------------------------------------------------------*/ |
f8e5fb60 | 3030 | |
6ab4b1ba D |
3031 | function createByte(codePoint, shift) { |
3032 | return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80); | |
3033 | } | |
3034 | ||
3035 | function encodeCodePoint(codePoint) { | |
3036 | if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence | |
3037 | return stringFromCharCode(codePoint); | |
3038 | } | |
3039 | var symbol = ''; | |
3040 | if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence | |
3041 | symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0); | |
3042 | } | |
3043 | else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence | |
3044 | symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0); | |
3045 | symbol += createByte(codePoint, 6); | |
3046 | } | |
3047 | else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence | |
3048 | symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0); | |
3049 | symbol += createByte(codePoint, 12); | |
3050 | symbol += createByte(codePoint, 6); | |
3051 | } | |
3052 | symbol += stringFromCharCode((codePoint & 0x3F) | 0x80); | |
3053 | return symbol; | |
3054 | } | |
d99e7823 | 3055 | |
6ab4b1ba D |
3056 | function utf8encode(string) { |
3057 | var codePoints = ucs2decode(string); | |
d99e7823 | 3058 | |
6ab4b1ba D |
3059 | // console.log(JSON.stringify(codePoints.map(function(x) { |
3060 | // return 'U+' + x.toString(16).toUpperCase(); | |
3061 | // }))); | |
f8e5fb60 | 3062 | |
6ab4b1ba D |
3063 | var length = codePoints.length; |
3064 | var index = -1; | |
3065 | var codePoint; | |
3066 | var byteString = ''; | |
3067 | while (++index < length) { | |
3068 | codePoint = codePoints[index]; | |
3069 | byteString += encodeCodePoint(codePoint); | |
3070 | } | |
3071 | return byteString; | |
3072 | } | |
d99e7823 | 3073 | |
6ab4b1ba | 3074 | /*--------------------------------------------------------------------------*/ |
d99e7823 | 3075 | |
6ab4b1ba D |
3076 | function readContinuationByte() { |
3077 | if (byteIndex >= byteCount) { | |
3078 | throw Error('Invalid byte index'); | |
3079 | } | |
d99e7823 | 3080 | |
6ab4b1ba D |
3081 | var continuationByte = byteArray[byteIndex] & 0xFF; |
3082 | byteIndex++; | |
d99e7823 | 3083 | |
6ab4b1ba D |
3084 | if ((continuationByte & 0xC0) == 0x80) { |
3085 | return continuationByte & 0x3F; | |
3086 | } | |
d99e7823 | 3087 | |
6ab4b1ba D |
3088 | // If we end up here, it’s not a continuation byte |
3089 | throw Error('Invalid continuation byte'); | |
3090 | } | |
d99e7823 | 3091 | |
6ab4b1ba D |
3092 | function decodeSymbol() { |
3093 | var byte1; | |
3094 | var byte2; | |
3095 | var byte3; | |
3096 | var byte4; | |
3097 | var codePoint; | |
d99e7823 | 3098 | |
6ab4b1ba D |
3099 | if (byteIndex > byteCount) { |
3100 | throw Error('Invalid byte index'); | |
3101 | } | |
d99e7823 | 3102 | |
6ab4b1ba D |
3103 | if (byteIndex == byteCount) { |
3104 | return false; | |
3105 | } | |
f8e5fb60 | 3106 | |
6ab4b1ba D |
3107 | // Read first byte |
3108 | byte1 = byteArray[byteIndex] & 0xFF; | |
3109 | byteIndex++; | |
f8e5fb60 | 3110 | |
6ab4b1ba D |
3111 | // 1-byte sequence (no continuation bytes) |
3112 | if ((byte1 & 0x80) == 0) { | |
3113 | return byte1; | |
3114 | } | |
f8e5fb60 | 3115 | |
6ab4b1ba D |
3116 | // 2-byte sequence |
3117 | if ((byte1 & 0xE0) == 0xC0) { | |
3118 | var byte2 = readContinuationByte(); | |
3119 | codePoint = ((byte1 & 0x1F) << 6) | byte2; | |
3120 | if (codePoint >= 0x80) { | |
3121 | return codePoint; | |
3122 | } else { | |
3123 | throw Error('Invalid continuation byte'); | |
3124 | } | |
3125 | } | |
d99e7823 | 3126 | |
6ab4b1ba D |
3127 | // 3-byte sequence (may include unpaired surrogates) |
3128 | if ((byte1 & 0xF0) == 0xE0) { | |
3129 | byte2 = readContinuationByte(); | |
3130 | byte3 = readContinuationByte(); | |
3131 | codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; | |
3132 | if (codePoint >= 0x0800) { | |
3133 | return codePoint; | |
3134 | } else { | |
3135 | throw Error('Invalid continuation byte'); | |
3136 | } | |
3137 | } | |
d99e7823 | 3138 | |
6ab4b1ba D |
3139 | // 4-byte sequence |
3140 | if ((byte1 & 0xF8) == 0xF0) { | |
3141 | byte2 = readContinuationByte(); | |
3142 | byte3 = readContinuationByte(); | |
3143 | byte4 = readContinuationByte(); | |
3144 | codePoint = ((byte1 & 0x0F) << 0x12) | (byte2 << 0x0C) | | |
3145 | (byte3 << 0x06) | byte4; | |
3146 | if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) { | |
3147 | return codePoint; | |
3148 | } | |
3149 | } | |
d99e7823 | 3150 | |
6ab4b1ba D |
3151 | throw Error('Invalid UTF-8 detected'); |
3152 | } | |
d99e7823 | 3153 | |
6ab4b1ba D |
3154 | var byteArray; |
3155 | var byteCount; | |
3156 | var byteIndex; | |
3157 | function utf8decode(byteString) { | |
3158 | byteArray = ucs2decode(byteString); | |
3159 | byteCount = byteArray.length; | |
3160 | byteIndex = 0; | |
3161 | var codePoints = []; | |
3162 | var tmp; | |
3163 | while ((tmp = decodeSymbol()) !== false) { | |
3164 | codePoints.push(tmp); | |
3165 | } | |
3166 | return ucs2encode(codePoints); | |
a74c0f0c | 3167 | } |
d99e7823 | 3168 | |
6ab4b1ba D |
3169 | /*--------------------------------------------------------------------------*/ |
3170 | ||
3171 | var utf8 = { | |
3172 | 'version': '2.0.0', | |
3173 | 'encode': utf8encode, | |
3174 | 'decode': utf8decode | |
3175 | }; | |
d99e7823 | 3176 | |
6ab4b1ba D |
3177 | // Some AMD build optimizers, like r.js, check for specific condition patterns |
3178 | // like the following: | |
3179 | if ( | |
3180 | typeof define == 'function' && | |
3181 | typeof define.amd == 'object' && | |
3182 | define.amd | |
3183 | ) { | |
3184 | define(function() { | |
3185 | return utf8; | |
3186 | }); | |
3187 | } else if (freeExports && !freeExports.nodeType) { | |
3188 | if (freeModule) { // in Node.js or RingoJS v0.8.0+ | |
3189 | freeModule.exports = utf8; | |
3190 | } else { // in Narwhal or RingoJS v0.7.0- | |
3191 | var object = {}; | |
3192 | var hasOwnProperty = object.hasOwnProperty; | |
3193 | for (var key in utf8) { | |
3194 | hasOwnProperty.call(utf8, key) && (freeExports[key] = utf8[key]); | |
3195 | } | |
3196 | } | |
3197 | } else { // in Rhino or a web browser | |
3198 | root.utf8 = utf8; | |
a74c0f0c | 3199 | } |
d99e7823 | 3200 | |
6ab4b1ba | 3201 | }(this)); |
d99e7823 | 3202 | |
6ab4b1ba | 3203 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) |
6ab4b1ba D |
3204 | },{}],21:[function(_dereq_,module,exports){ |
3205 | ||
3206 | /** | |
3207 | * Module dependencies. | |
3208 | */ | |
3209 | ||
3210 | var global = _dereq_('global'); | |
d99e7823 | 3211 | |
a74c0f0c | 3212 | /** |
6ab4b1ba | 3213 | * Module exports. |
a74c0f0c | 3214 | * |
6ab4b1ba D |
3215 | * Logic borrowed from Modernizr: |
3216 | * | |
3217 | * - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js | |
a74c0f0c | 3218 | */ |
d99e7823 | 3219 | |
6ab4b1ba D |
3220 | try { |
3221 | module.exports = 'XMLHttpRequest' in global && | |
3222 | 'withCredentials' in new global.XMLHttpRequest(); | |
3223 | } catch (err) { | |
3224 | // if XMLHttp support is disabled in IE then it will throw | |
3225 | // when trying to create | |
3226 | module.exports = false; | |
3227 | } | |
3228 | ||
e65c2242 JA |
3229 | },{"global":22}],22:[function(_dereq_,module,exports){ |
3230 | ||
3231 | /** | |
3232 | * Returns `this`. Execute this without a "context" (i.e. without it being | |
3233 | * attached to an object of the left-hand side), and `this` points to the | |
3234 | * "global" scope of the current JS execution. | |
3235 | */ | |
3236 | ||
3237 | module.exports = (function () { return this; })(); | |
3238 | ||
3239 | },{}],23:[function(_dereq_,module,exports){ | |
6ab4b1ba D |
3240 | |
3241 | var indexOf = [].indexOf; | |
3242 | ||
3243 | module.exports = function(arr, obj){ | |
3244 | if (indexOf) return arr.indexOf(obj); | |
3245 | for (var i = 0; i < arr.length; ++i) { | |
3246 | if (arr[i] === obj) return i; | |
3247 | } | |
3248 | return -1; | |
d99e7823 | 3249 | }; |
6ab4b1ba D |
3250 | },{}],24:[function(_dereq_,module,exports){ |
3251 | (function (global){ | |
d99e7823 | 3252 | /** |
6ab4b1ba | 3253 | * JSON parse. |
d99e7823 | 3254 | * |
6ab4b1ba | 3255 | * @see Based on jQuery#parseJSON (MIT) and JSON2 |
d99e7823 D |
3256 | * @api private |
3257 | */ | |
3258 | ||
6ab4b1ba D |
3259 | var rvalidchars = /^[\],:{}\s]*$/; |
3260 | var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; | |
3261 | var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; | |
3262 | var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g; | |
3263 | var rtrimLeft = /^\s+/; | |
3264 | var rtrimRight = /\s+$/; | |
d99e7823 | 3265 | |
6ab4b1ba D |
3266 | module.exports = function parsejson(data) { |
3267 | if ('string' != typeof data || !data) { | |
3268 | return null; | |
a74c0f0c | 3269 | } |
d99e7823 | 3270 | |
6ab4b1ba | 3271 | data = data.replace(rtrimLeft, '').replace(rtrimRight, ''); |
d99e7823 | 3272 | |
6ab4b1ba D |
3273 | // Attempt to parse using the native JSON parser first |
3274 | if (global.JSON && JSON.parse) { | |
3275 | return JSON.parse(data); | |
a74c0f0c | 3276 | } |
d99e7823 | 3277 | |
6ab4b1ba D |
3278 | if (rvalidchars.test(data.replace(rvalidescape, '@') |
3279 | .replace(rvalidtokens, ']') | |
3280 | .replace(rvalidbraces, ''))) { | |
3281 | return (new Function('return ' + data))(); | |
a74c0f0c | 3282 | } |
a74c0f0c | 3283 | }; |
6ab4b1ba D |
3284 | }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) |
3285 | },{}],25:[function(_dereq_,module,exports){ | |
d99e7823 | 3286 | /** |
6ab4b1ba D |
3287 | * Compiles a querystring |
3288 | * Returns string representation of the object | |
f8e5fb60 | 3289 | * |
6ab4b1ba D |
3290 | * @param {Object} |
3291 | * @api private | |
d99e7823 D |
3292 | */ |
3293 | ||
6ab4b1ba D |
3294 | exports.encode = function (obj) { |
3295 | var str = ''; | |
d99e7823 | 3296 | |
6ab4b1ba D |
3297 | for (var i in obj) { |
3298 | if (obj.hasOwnProperty(i)) { | |
3299 | if (str.length) str += '&'; | |
3300 | str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]); | |
a74c0f0c | 3301 | } |
f8e5fb60 | 3302 | } |
a74c0f0c | 3303 | |
6ab4b1ba | 3304 | return str; |
d99e7823 D |
3305 | }; |
3306 | ||
3307 | /** | |
6ab4b1ba D |
3308 | * Parses a simple querystring into an object |
3309 | * | |
3310 | * @param {String} qs | |
3311 | * @api private | |
d99e7823 D |
3312 | */ |
3313 | ||
6ab4b1ba D |
3314 | exports.decode = function(qs){ |
3315 | var qry = {}; | |
3316 | var pairs = qs.split('&'); | |
3317 | for (var i = 0, l = pairs.length; i < l; i++) { | |
3318 | var pair = pairs[i].split('='); | |
3319 | qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); | |
3320 | } | |
3321 | return qry; | |
3322 | }; | |
d99e7823 | 3323 | |
6ab4b1ba | 3324 | },{}],26:[function(_dereq_,module,exports){ |
d99e7823 | 3325 | /** |
6ab4b1ba | 3326 | * Parses an URI |
a74c0f0c | 3327 | * |
6ab4b1ba | 3328 | * @author Steven Levithan <stevenlevithan.com> (MIT license) |
a74c0f0c | 3329 | * @api private |
d99e7823 D |
3330 | */ |
3331 | ||
6ab4b1ba | 3332 | var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; |
d99e7823 | 3333 | |
6ab4b1ba D |
3334 | var parts = [ |
3335 | 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host' | |
3336 | , 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor' | |
3337 | ]; | |
a74c0f0c | 3338 | |
6ab4b1ba D |
3339 | module.exports = function parseuri(str) { |
3340 | var m = re.exec(str || '') | |
3341 | , uri = {} | |
3342 | , i = 14; | |
a74c0f0c | 3343 | |
6ab4b1ba D |
3344 | while (i--) { |
3345 | uri[parts[i]] = m[i] || ''; | |
3346 | } | |
d99e7823 | 3347 | |
6ab4b1ba | 3348 | return uri; |
a74c0f0c | 3349 | }; |
d99e7823 | 3350 | |
6ab4b1ba D |
3351 | },{}],27:[function(_dereq_,module,exports){ |
3352 | ||
d99e7823 | 3353 | /** |
6ab4b1ba | 3354 | * Module dependencies. |
d99e7823 D |
3355 | */ |
3356 | ||
6ab4b1ba | 3357 | var global = (function() { return this; })(); |
d99e7823 | 3358 | |
6ab4b1ba D |
3359 | /** |
3360 | * WebSocket constructor. | |
3361 | */ | |
3362 | ||
3363 | var WebSocket = global.WebSocket || global.MozWebSocket; | |
d99e7823 | 3364 | |
6ab4b1ba D |
3365 | /** |
3366 | * Module exports. | |
3367 | */ | |
a74c0f0c | 3368 | |
6ab4b1ba | 3369 | module.exports = WebSocket ? ws : null; |
a74c0f0c | 3370 | |
6ab4b1ba D |
3371 | /** |
3372 | * WebSocket constructor. | |
3373 | * | |
3374 | * The third `opts` options object gets ignored in web browsers, since it's | |
3375 | * non-standard, and throws a TypeError if passed to the constructor. | |
3376 | * See: https://github.com/einaros/ws/issues/227 | |
3377 | * | |
3378 | * @param {String} uri | |
3379 | * @param {Array} protocols (optional) | |
3380 | * @param {Object) opts (optional) | |
3381 | * @api public | |
3382 | */ | |
a74c0f0c | 3383 | |
6ab4b1ba D |
3384 | function ws(uri, protocols, opts) { |
3385 | var instance; | |
3386 | if (protocols) { | |
3387 | instance = new WebSocket(uri, protocols); | |
3388 | } else { | |
3389 | instance = new WebSocket(uri); | |
3390 | } | |
3391 | return instance; | |
3392 | } | |
a74c0f0c | 3393 | |
6ab4b1ba | 3394 | if (WebSocket) ws.prototype = WebSocket.prototype; |
a74c0f0c | 3395 | |
6ab4b1ba D |
3396 | },{}]},{},[1]) |
3397 | (1) | |
e65c2242 | 3398 | }); |