Update backbone from 1.0.0 to 1.1.2
[KiwiIRC.git] / client / assets / libs / engine.io.js
CommitLineData
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
3module.exports = _dereq_('./lib/');
4
5},{"./lib/":2}],2:[function(_dereq_,module,exports){
6
7module.exports = _dereq_('./socket');
d99e7823 8
f8e5fb60 9/**
6ab4b1ba 10 * Exports parser
a74c0f0c 11 *
a74c0f0c 12 * @api public
6ab4b1ba 13 *
f8e5fb60 14 */
6ab4b1ba 15module.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 23var transports = _dereq_('./transports');
e65c2242 24var Emitter = _dereq_('component-emitter');
6ab4b1ba
D
25var debug = _dereq_('debug')('engine.io-client:socket');
26var index = _dereq_('indexof');
27var parser = _dereq_('engine.io-parser');
28var parseuri = _dereq_('parseuri');
29var parsejson = _dereq_('parsejson');
30var parseqs = _dereq_('parseqs');
d99e7823
D
31
32/**
6ab4b1ba 33 * Module exports.
d99e7823
D
34 */
35
6ab4b1ba 36module.exports = Socket;
d99e7823
D
37
38/**
6ab4b1ba
D
39 * Noop function.
40 *
41 * @api private
d99e7823
D
42 */
43
6ab4b1ba 44function 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
54function 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
106Socket.priorWebsocketSuccess = false;
107
108/**
109 * Mix in `Emitter`.
110 */
111
112Emitter(Socket.prototype);
d99e7823
D
113
114/**
6ab4b1ba 115 * Protocol version.
d99e7823 116 *
6ab4b1ba
D
117 * @api public
118 */
119
120Socket.protocol = parser.protocol; // this is an int
121
122/**
123 * Expose deps for legacy compatibility
124 * and standalone browser access.
125 */
126
127Socket.Socket = Socket;
128Socket.Transport = _dereq_('./transport');
129Socket.transports = _dereq_('./transports');
130Socket.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
140Socket.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
171function 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
186Socket.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
205Socket.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
240Socket.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
358Socket.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
381Socket.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
422Socket.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
445Socket.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
461Socket.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
477Socket.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
487Socket.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
515Socket.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
536Socket.prototype.write =
537Socket.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
551Socket.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
565Socket.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
581Socket.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
594Socket.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
639Socket.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 653var parser = _dereq_('engine.io-parser');
e65c2242 654var Emitter = _dereq_('component-emitter');
d99e7823 655
a74c0f0c 656/**
6ab4b1ba 657 * Module exports.
a74c0f0c
D
658 */
659
6ab4b1ba 660module.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
669function 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 686Emitter(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 693Transport.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
703Transport.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
717Transport.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
732Transport.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
748Transport.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
762Transport.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
775Transport.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
789Transport.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
799Transport.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
810var XMLHttpRequest = _dereq_('xmlhttprequest');
811var XHR = _dereq_('./polling-xhr');
812var JSONP = _dereq_('./polling-jsonp');
813var websocket = _dereq_('./websocket');
d99e7823 814
f8e5fb60 815/**
6ab4b1ba 816 * Export transports.
f8e5fb60
JA
817 */
818
6ab4b1ba
D
819exports.polling = polling;
820exports.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
829function 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
863var Polling = _dereq_('./polling');
e65c2242 864var inherit = _dereq_('component-inherit');
d99e7823 865
f8e5fb60 866/**
6ab4b1ba 867 * Module exports.
a74c0f0c
D
868 */
869
6ab4b1ba 870module.exports = JSONPPolling;
a74c0f0c
D
871
872/**
6ab4b1ba 873 * Cached regular expressions.
f8e5fb60
JA
874 */
875
6ab4b1ba
D
876var rNewline = /\n/g;
877var rEscapedNewline = /\\n/g;
878
879/**
880 * Global JSONP callbacks.
881 */
882
883var callbacks;
d99e7823
D
884
885/**
6ab4b1ba 886 * Callbacks count.
d99e7823
D
887 */
888
6ab4b1ba 889var index = 0;
d99e7823 890
a74c0f0c 891/**
6ab4b1ba 892 * Noop.
a74c0f0c
D
893 */
894
6ab4b1ba 895function 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
904function 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
941inherit(JSONPPolling, Polling);
942
943/*
944 * JSONP only supports binary as base64 encoded strings
945 */
946
947JSONPPolling.prototype.supportsBinary = false;
948
949/**
950 * Closes the socket.
d99e7823 951 *
d99e7823
D
952 * @api private
953 */
954
6ab4b1ba
D
955JSONPPolling.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 975JSONPPolling.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
1013JSONPPolling.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
1098var XMLHttpRequest = _dereq_('xmlhttprequest');
1099var Polling = _dereq_('./polling');
e65c2242
JA
1100var Emitter = _dereq_('component-emitter');
1101var inherit = _dereq_('component-inherit');
6ab4b1ba 1102var debug = _dereq_('debug')('engine.io-client:polling-xhr');
d99e7823
D
1103
1104/**
6ab4b1ba 1105 * Module exports.
a74c0f0c
D
1106 */
1107
6ab4b1ba
D
1108module.exports = XHR;
1109module.exports.Request = Request;
a74c0f0c
D
1110
1111/**
6ab4b1ba 1112 * Empty function
d99e7823
D
1113 */
1114
6ab4b1ba 1115function 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
1124function 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
1145inherit(XHR, Polling);
d99e7823
D
1146
1147/**
6ab4b1ba 1148 * XHR supports binary
d99e7823
D
1149 */
1150
6ab4b1ba 1151XHR.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
1160XHR.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
1177XHR.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
1194XHR.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
1214function 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 1228Emitter(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
1236Request.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
1320Request.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
1331Request.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
1342Request.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
1353Request.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
1377Request.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
1387if (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
1397function 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
1411var Transport = _dereq_('../transport');
1412var parseqs = _dereq_('parseqs');
1413var parser = _dereq_('engine.io-parser');
e65c2242 1414var inherit = _dereq_('component-inherit');
6ab4b1ba 1415var debug = _dereq_('debug')('engine.io-client:polling');
d99e7823
D
1416
1417/**
1418 * Module exports.
1419 */
1420
1421module.exports = Polling;
1422
1423/**
6ab4b1ba 1424 * Is XHR2 supported?
d99e7823
D
1425 */
1426
6ab4b1ba
D
1427var 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
1440function 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 1452inherit(Polling, Transport);
d99e7823
D
1453
1454/**
1455 * Transport name.
1456 */
1457
1458Polling.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
1467Polling.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
1478Polling.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
1521Polling.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
1534Polling.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
1576Polling.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
1603Polling.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
1623Polling.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
1658var Transport = _dereq_('../transport');
1659var parser = _dereq_('engine.io-parser');
1660var parseqs = _dereq_('parseqs');
e65c2242 1661var inherit = _dereq_('component-inherit');
6ab4b1ba 1662var 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
1670var WebSocket = _dereq_('ws');
1671
1672/**
1673 * Module exports.
1674 */
1675
1676module.exports = WS;
1677
1678/**
1679 * WebSocket transport constructor.
1680 *
1681 * @api {Object} connection options
1682 * @api public
1683 */
1684
1685function 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
1697inherit(WS, Transport);
1698
1699/**
1700 * Transport name.
1701 *
1702 * @api public
1703 */
1704
1705WS.prototype.name = 'websocket';
1706
1707/*
1708 * WebSockets support binary
1709 */
1710
1711WS.prototype.supportsBinary = true;
1712
1713/**
1714 * Opens socket.
1715 *
1716 * @api private
1717 */
1718
1719WS.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
1746WS.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
1770if ('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
1787WS.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
1820WS.prototype.onClose = function(){
1821 Transport.prototype.onClose.call(this);
1822};
1823
1824/**
1825 * Closes socket.
1826 *
1827 * @api private
1828 */
1829
1830WS.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
1842WS.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
1880WS.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
1886var hasCORS = _dereq_('has-cors');
1887
1888module.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
1911var BlobBuilder = global.BlobBuilder
1912 || global.WebKitBlobBuilder
1913 || global.MSBlobBuilder
1914 || global.MozBlobBuilder;
1915
1916/**
1917 * Check if Blob constructor is supported
1918 */
1919
1920var 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
1933var blobBuilderSupported = BlobBuilder
1934 && BlobBuilder.prototype.append
1935 && BlobBuilder.prototype.getBlob;
d99e7823 1936
6ab4b1ba
D
1937function 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
1947module.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
1964module.exports = Emitter;
1965
1966/**
1967 * Initialize a new `Emitter`.
f8e5fb60 1968 *
6ab4b1ba 1969 * @api public
f8e5fb60
JA
1970 */
1971
6ab4b1ba
D
1972function 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
1984function 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
2000Emitter.prototype.on =
2001Emitter.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 2018Emitter.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
2042Emitter.prototype.off =
2043Emitter.prototype.removeListener =
e65c2242
JA
2044Emitter.prototype.removeAllListeners =
2045Emitter.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
2084Emitter.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
2107Emitter.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
2120Emitter.prototype.hasListeners = function(event){
2121 return !! this.listeners(event).length;
f8e5fb60 2122};
d99e7823 2123
e65c2242
JA
2124},{}],13:[function(_dereq_,module,exports){
2125
2126module.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
2138module.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
2148function 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
2175debug.names = [];
2176debug.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
2186debug.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
2211debug.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
2223debug.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
2242debug.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
2260function coerce(val) {
2261 if (val instanceof Error) return val.stack || val.message;
2262 return val;
2263}
2264
2265// persist
2266
2267try {
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
2277var keys = _dereq_('./keys');
2278var sliceBuffer = _dereq_('arraybuffer.slice');
2279var base64encoder = _dereq_('base64-arraybuffer');
2280var after = _dereq_('after');
2281var 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 2290var isAndroid = navigator.userAgent.match(/Android/i);
d99e7823 2291
6ab4b1ba
D
2292/**
2293 * Current protocol version.
2294 */
a74c0f0c 2295
6ab4b1ba 2296exports.protocol = 2;
d99e7823
D
2297
2298/**
6ab4b1ba 2299 * Packet types.
d99e7823
D
2300 */
2301
6ab4b1ba
D
2302var 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 2312var packetslist = keys(packets);
a74c0f0c 2313
d99e7823 2314/**
6ab4b1ba 2315 * Premade error packet.
d99e7823
D
2316 */
2317
6ab4b1ba 2318var 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 2324var 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
2342exports.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
2374function 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
2391function 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
2404function 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
2427exports.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
2461exports.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
2498exports.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
2529exports.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
2566function 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
2590exports.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
2666exports.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
2728exports.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
2774exports.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
2827module.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
2840module.exports = after
2841
2842function 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
2867function 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
2877module.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
3210var 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
3220try {
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
3237module.exports = (function () { return this; })();
3238
3239},{}],23:[function(_dereq_,module,exports){
6ab4b1ba
D
3240
3241var indexOf = [].indexOf;
3242
3243module.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
3259var rvalidchars = /^[\],:{}\s]*$/;
3260var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
3261var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
3262var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g;
3263var rtrimLeft = /^\s+/;
3264var rtrimRight = /\s+$/;
d99e7823 3265
6ab4b1ba
D
3266module.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
3294exports.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
3314exports.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 3332var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
d99e7823 3333
6ab4b1ba
D
3334var parts = [
3335 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host'
3336 , 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
3337];
a74c0f0c 3338
6ab4b1ba
D
3339module.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 3357var global = (function() { return this; })();
d99e7823 3358
6ab4b1ba
D
3359/**
3360 * WebSocket constructor.
3361 */
3362
3363var WebSocket = global.WebSocket || global.MozWebSocket;
d99e7823 3364
6ab4b1ba
D
3365/**
3366 * Module exports.
3367 */
a74c0f0c 3368
6ab4b1ba 3369module.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
3384function 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 3394if (WebSocket) ws.prototype = WebSocket.prototype;
a74c0f0c 3395
6ab4b1ba
D
3396},{}]},{},[1])
3397(1)
e65c2242 3398});