UNPKG

56.8 kBJavaScriptView Raw
1(function(){var global = this;function debug(){return debug};function require(p, parent){ var path = require.resolve(p) , mod = require.modules[path]; if (!mod) throw new Error('failed to require "' + p + '" from ' + parent); if (!mod.exports) { mod.exports = {}; mod.call(mod.exports, mod, mod.exports, require.relative(path), global); } return mod.exports;}require.modules = {};require.resolve = function(path){ var orig = path , reg = path + '.js' , index = path + '/index.js'; return require.modules[reg] && reg || require.modules[index] && index || orig;};require.register = function(path, fn){ require.modules[path] = fn;};require.relative = function(parent) { return function(p){ if ('debug' == p) return debug; if ('.' != p.charAt(0)) return require(p); var path = parent.split('/') , segs = p.split('/'); path.pop(); for (var i = 0; i < segs.length; i++) { var seg = segs[i]; if ('..' == seg) path.pop(); else if ('.' != seg) path.push(seg); } return require(path.join('/'), parent); };};require.register("engine.io-client.js", function(module, exports, require, global){
2
3/**
4 * Client version.
5 *
6 * @api public.
7 */
8
9exports.version = '0.3.10';
10
11/**
12 * Protocol version.
13 *
14 * @api public.
15 */
16
17exports.protocol = 1;
18
19/**
20 * Utils.
21 *
22 * @api public
23 */
24
25exports.util = require('./util');
26
27/**
28 * Parser.
29 *
30 * @api public
31 */
32
33exports.parser = require('./parser');
34
35/**
36 * Socket constructor.
37 *
38 * @api public.
39 */
40
41exports.Socket = require('./socket');
42
43/**
44 * Export EventEmitter.
45 */
46
47exports.EventEmitter = require('./event-emitter');
48
49/**
50 * Export Transport.
51 */
52
53exports.Transport = require('./transport');
54
55/**
56 * Export transports
57 */
58
59exports.transports = require('./transports');
60
61});require.register("event-emitter.js", function(module, exports, require, global){
62
63/**
64 * Module exports.
65 */
66
67module.exports = EventEmitter;
68
69/**
70 * Event emitter constructor.
71 *
72 * @api public.
73 */
74
75function EventEmitter () {};
76
77/**
78 * Adds a listener
79 *
80 * @api public
81 */
82
83EventEmitter.prototype.on = function (name, fn) {
84 if (!this.$events) {
85 this.$events = {};
86 }
87
88 if (!this.$events[name]) {
89 this.$events[name] = fn;
90 } else if (isArray(this.$events[name])) {
91 this.$events[name].push(fn);
92 } else {
93 this.$events[name] = [this.$events[name], fn];
94 }
95
96 return this;
97};
98
99EventEmitter.prototype.addListener = EventEmitter.prototype.on;
100
101/**
102 * Adds a volatile listener.
103 *
104 * @api public
105 */
106
107EventEmitter.prototype.once = function (name, fn) {
108 var self = this;
109
110 function on () {
111 self.removeListener(name, on);
112 fn.apply(this, arguments);
113 };
114
115 on.listener = fn;
116 this.on(name, on);
117
118 return this;
119};
120
121/**
122 * Removes a listener.
123 *
124 * @api public
125 */
126
127EventEmitter.prototype.removeListener = function (name, fn) {
128 if (this.$events && this.$events[name]) {
129 var list = this.$events[name];
130
131 if (isArray(list)) {
132 var pos = -1;
133
134 for (var i = 0, l = list.length; i < l; i++) {
135 if (list[i] === fn || (list[i].listener && list[i].listener === fn)) {
136 pos = i;
137 break;
138 }
139 }
140
141 if (pos < 0) {
142 return this;
143 }
144
145 list.splice(pos, 1);
146
147 if (!list.length) {
148 delete this.$events[name];
149 }
150 } else if (list === fn || (list.listener && list.listener === fn)) {
151 delete this.$events[name];
152 }
153 }
154
155 return this;
156};
157
158/**
159 * Removes all listeners for an event.
160 *
161 * @api public
162 */
163
164EventEmitter.prototype.removeAllListeners = function (name) {
165 if (name === undefined) {
166 this.$events = {};
167 return this;
168 }
169
170 if (this.$events && this.$events[name]) {
171 this.$events[name] = null;
172 }
173
174 return this;
175};
176
177/**
178 * Gets all listeners for a certain event.
179 *
180 * @api publci
181 */
182
183EventEmitter.prototype.listeners = function (name) {
184 if (!this.$events) {
185 this.$events = {};
186 }
187
188 if (!this.$events[name]) {
189 this.$events[name] = [];
190 }
191
192 if (!isArray(this.$events[name])) {
193 this.$events[name] = [this.$events[name]];
194 }
195
196 return this.$events[name];
197};
198
199/**
200 * Emits an event.
201 *
202 * @api public
203 */
204
205EventEmitter.prototype.emit = function (name) {
206 if (!this.$events) {
207 return false;
208 }
209
210 var handler = this.$events[name];
211
212 if (!handler) {
213 return false;
214 }
215
216 var args = Array.prototype.slice.call(arguments, 1);
217
218 if ('function' == typeof handler) {
219 handler.apply(this, args);
220 } else if (isArray(handler)) {
221 var listeners = handler.slice();
222
223 for (var i = 0, l = listeners.length; i < l; i++) {
224 listeners[i].apply(this, args);
225 }
226 } else {
227 return false;
228 }
229
230 return true;
231};
232
233/**
234 * Checks for Array type.
235 *
236 * @param {Object} object
237 * @api private
238 */
239
240function isArray (obj) {
241 return '[object Array]' == Object.prototype.toString.call(obj);
242};
243
244/**
245 * Compatibility with WebSocket
246 */
247
248EventEmitter.prototype.addEventListener = EventEmitter.prototype.on;
249EventEmitter.prototype.removeEventListener = EventEmitter.prototype.removeListener;
250EventEmitter.prototype.dispatchEvent = EventEmitter.prototype.emit;
251
252});require.register("parser.js", function(module, exports, require, global){
253/**
254 * Module dependencies.
255 */
256
257var util = require('./util')
258
259/**
260 * Packet types.
261 */
262
263var packets = exports.packets = {
264 open: 0 // non-ws
265 , close: 1 // non-ws
266 , ping: 2
267 , pong: 3
268 , message: 4
269 , upgrade: 5
270 , noop: 6
271};
272
273var packetslist = util.keys(packets);
274
275/**
276 * Premade error packet.
277 */
278
279var err = { type: 'error', data: 'parser error' }
280
281/**
282 * Encodes a packet.
283 *
284 * <packet type id> [ `:` <data> ]
285 *
286 * Example:
287 *
288 * 5:hello world
289 * 3
290 * 4
291 *
292 * @api private
293 */
294
295exports.encodePacket = function (packet) {
296 var encoded = packets[packet.type]
297
298 // data fragment is optional
299 if (undefined !== packet.data) {
300 encoded += String(packet.data);
301 }
302
303 return '' + encoded;
304};
305
306/**
307 * Decodes a packet.
308 *
309 * @return {Object} with `type` and `data` (if any)
310 * @api private
311 */
312
313exports.decodePacket = function (data) {
314 var type = data.charAt(0);
315
316 if (Number(type) != type || !packetslist[type]) {
317 return err;
318 }
319
320 if (data.length > 1) {
321 return { type: packetslist[type], data: data.substring(1) };
322 } else {
323 return { type: packetslist[type] };
324 }
325};
326
327/**
328 * Encodes multiple messages (payload).
329 *
330 * <length>:data
331 *
332 * Example:
333 *
334 * 11:hello world2:hi
335 *
336 * @param {Array} packets
337 * @api private
338 */
339
340exports.encodePayload = function (packets) {
341 if (!packets.length) {
342 return '0:';
343 }
344
345 var encoded = ''
346 , message
347
348 for (var i = 0, l = packets.length; i < l; i++) {
349 message = exports.encodePacket(packets[i]);
350 encoded += message.length + ':' + message;
351 }
352
353 return encoded;
354};
355
356/*
357 * Decodes data when a payload is maybe expected.
358 *
359 * @param {String} data
360 * @return {Array} packets
361 * @api public
362 */
363
364exports.decodePayload = function (data) {
365 if (data == '') {
366 // parser error - ignoring payload
367 return [err];
368 }
369
370 var packets = []
371 , length = ''
372 , n, msg, packet
373
374 for (var i = 0, l = data.length; i < l; i++) {
375 var chr = data.charAt(i)
376
377 if (':' != chr) {
378 length += chr;
379 } else {
380 if ('' == length || (length != (n = Number(length)))) {
381 // parser error - ignoring payload
382 return [err];
383 }
384
385 msg = data.substr(i + 1, n);
386
387 if (length != msg.length) {
388 // parser error - ignoring payload
389 return [err];
390 }
391
392 if (msg.length) {
393 packet = exports.decodePacket(msg);
394
395 if (err.type == packet.type && err.data == packet.data) {
396 // parser error in individual packet - ignoring payload
397 return [err];
398 }
399
400 packets.push(packet);
401 }
402
403 // advance cursor
404 i += n;
405 length = ''
406 }
407 }
408
409 if (length != '') {
410 // parser error - ignoring payload
411 return [err];
412 }
413
414 return packets;
415};
416
417});require.register("socket.js", function(module, exports, require, global){
418/**
419 * Module dependencies.
420 */
421
422var util = require('./util')
423 , transports = require('./transports')
424 , debug = require('debug')('engine-client:socket')
425 , EventEmitter = require('./event-emitter');
426
427/**
428 * Module exports.
429 */
430
431module.exports = Socket;
432
433/**
434 * Socket constructor.
435 *
436 * @param {Object} options
437 * @api public
438 */
439
440function Socket (opts) {
441 if ('string' == typeof opts) {
442 var uri = util.parseUri(opts);
443 opts = arguments[1] || {};
444 opts.host = uri.host;
445 opts.secure = uri.protocol == 'https' || uri.protocol == 'wss';
446 opts.port = uri.port;
447 }
448
449 opts = opts || {};
450 this.secure = null != opts.secure ? opts.secure : (global.location && 'https:' == location.protocol);
451 this.host = opts.host || opts.hostname || (global.location ? location.hostname : 'localhost');
452 this.port = opts.port || (global.location && location.port ? location.port : (this.secure ? 443 : 80));
453 this.query = opts.query || {};
454 this.query.uid = rnd();
455 this.upgrade = false !== opts.upgrade;
456 this.resource = opts.resource || 'default';
457 this.path = (opts.path || '/engine.io').replace(/\/$/, '');
458 this.path += '/' + this.resource + '/';
459 this.forceJSONP = !!opts.forceJSONP;
460 this.timestampParam = opts.timestampParam || 't';
461 this.timestampRequests = !!opts.timestampRequests;
462 this.flashPath = opts.flashPath || '';
463 this.transports = opts.transports || ['polling', 'websocket', 'flashsocket'];
464 this.readyState = '';
465 this.writeBuffer = [];
466 this.policyPort = opts.policyPort || 843;
467 this.open();
468
469 Socket.sockets.push(this);
470 Socket.sockets.evs.emit('add', this);
471};
472
473/**
474 * Inherits from EventEmitter.
475 */
476
477util.inherits(Socket, EventEmitter);
478
479/**
480 * Static EventEmitter.
481 */
482
483Socket.sockets = [];
484Socket.sockets.evs = new EventEmitter;
485
486/**
487 * Creates transport of the given type.
488 *
489 * @param {String} transport name
490 * @return {Transport}
491 * @api private
492 */
493
494Socket.prototype.createTransport = function (name) {
495 debug('creating transport "%s"', name);
496 var query = clone(this.query);
497 query.transport = name;
498
499 if (this.id) {
500 query.sid = this.id;
501 }
502
503 var transport = new transports[name]({
504 host: this.host
505 , port: this.port
506 , secure: this.secure
507 , path: this.path
508 , query: query
509 , forceJSONP: this.forceJSONP
510 , timestampRequests: this.timestampRequests
511 , timestampParam: this.timestampParam
512 , flashPath: this.flashPath
513 , policyPort: this.policyPort
514 });
515
516 return transport;
517};
518
519function clone (obj) {
520 var o = {};
521 for (var i in obj) {
522 if (obj.hasOwnProperty(i)) {
523 o[i] = obj[i];
524 }
525 }
526 return o;
527}
528
529/**
530 * Initializes transport to use and starts probe.
531 *
532 * @api private
533 */
534
535Socket.prototype.open = function () {
536 this.readyState = 'opening';
537 var transport = this.createTransport(this.transports[0]);
538 transport.open();
539 this.setTransport(transport);
540};
541
542/**
543 * Sets the current transport. Disables the existing one (if any).
544 *
545 * @api private
546 */
547
548Socket.prototype.setTransport = function (transport) {
549 var self = this;
550
551 if (this.transport) {
552 debug('clearing existing transport');
553 this.transport.removeAllListeners();
554 }
555
556 // set up transport
557 this.transport = transport;
558
559 // set up transport listeners
560 transport
561 .on('drain', function () {
562 self.flush();
563 })
564 .on('packet', function (packet) {
565 self.onPacket(packet);
566 })
567 .on('error', function (e) {
568 self.onError(e);
569 })
570 .on('close', function () {
571 self.onClose('transport close');
572 });
573};
574
575/**
576 * Probes a transport.
577 *
578 * @param {String} transport name
579 * @api private
580 */
581
582Socket.prototype.probe = function (name) {
583 debug('probing transport "%s"', name);
584 var transport = this.createTransport(name, { probe: 1 })
585 , failed = false
586 , self = this;
587
588 transport.once('open', function () {
589 if (failed) return;
590
591 debug('probe transport "%s" opened', name);
592 transport.send([{ type: 'ping', data: 'probe' }]);
593 transport.once('packet', function (msg) {
594 if (failed) return;
595 if ('pong' == msg.type && 'probe' == msg.data) {
596 debug('probe transport "%s" pong', name);
597 self.upgrading = true;
598 self.emit('upgrading', transport);
599
600 debug('pausing current transport "%s"', self.transport.name);
601 self.transport.pause(function () {
602 if (failed) return;
603 if ('closed' == self.readyState || 'closing' == self.readyState) {
604 return;
605 }
606 debug('changing transport and sending upgrade packet');
607 transport.removeListener('error', onerror);
608 self.emit('upgrade', transport);
609 self.setTransport(transport);
610 transport.send([{ type: 'upgrade' }]);
611 transport = null;
612 self.upgrading = false;
613 self.flush();
614 });
615 } else {
616 debug('probe transport "%s" failed', name);
617 var err = new Error('probe error');
618 err.transport = transport.name;
619 self.emit('error', err);
620 }
621 });
622 });
623
624 transport.once('error', onerror);
625 function onerror(err) {
626 if (failed) return;
627
628 // Any callback called by transport should be ignored since now
629 failed = true;
630
631 var error = new Error('probe error: ' + err);
632 error.transport = transport.name;
633
634 transport.close();
635 transport = null;
636
637 debug('probe transport "%s" failed because of error: %s', name, err);
638
639 self.emit('error', error);
640 };
641
642 transport.open();
643
644 this.once('close', function () {
645 if (transport) {
646 debug('socket closed prematurely - aborting probe');
647 failed = true;
648 transport.close();
649 transport = null;
650 }
651 });
652
653 this.once('upgrading', function (to) {
654 if (transport && to.name != transport.name) {
655 debug('"%s" works - aborting "%s"', to.name, transport.name);
656 transport.close();
657 transport = null;
658 }
659 });
660};
661
662/**
663 * Called when connection is deemed open.
664 *
665 * @api public
666 */
667
668Socket.prototype.onOpen = function () {
669 debug('socket open');
670 this.readyState = 'open';
671 this.emit('open');
672 this.onopen && this.onopen.call(this);
673 this.flush();
674
675 // we check for `readyState` in case an `open`
676 // listener alreay closed the socket
677 if ('open' == this.readyState && this.upgrade && this.transport.pause) {
678 debug('starting upgrade probes');
679 for (var i = 0, l = this.upgrades.length; i < l; i++) {
680 this.probe(this.upgrades[i]);
681 }
682 }
683};
684
685/**
686 * Handles a packet.
687 *
688 * @api private
689 */
690
691Socket.prototype.onPacket = function (packet) {
692 if ('opening' == this.readyState || 'open' == this.readyState) {
693 debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
694
695 this.emit('packet', packet);
696
697 // Socket is live - any packet counts
698 this.emit('heartbeat');
699
700 switch (packet.type) {
701 case 'open':
702 this.onHandshake(util.parseJSON(packet.data));
703 break;
704
705 case 'pong':
706 this.ping();
707 break;
708
709 case 'error':
710 var err = new Error('server error');
711 err.code = packet.data;
712 this.emit('error', err);
713 break;
714
715 case 'message':
716 this.emit('message', packet.data);
717 var event = { data: packet.data };
718 event.toString = function () {
719 return packet.data;
720 };
721 this.onmessage && this.onmessage.call(this, event);
722 break;
723 }
724 } else {
725 debug('packet received with socket readyState "%s"', this.readyState);
726 }
727};
728
729/**
730 * Called upon handshake completion.
731 *
732 * @param {Object} handshake obj
733 * @api private
734 */
735
736Socket.prototype.onHandshake = function (data) {
737 this.emit('handshake', data);
738 this.id = data.sid;
739 this.transport.query.sid = data.sid;
740 this.upgrades = data.upgrades;
741 this.pingInterval = data.pingInterval;
742 this.pingTimeout = data.pingTimeout;
743 this.onOpen();
744 this.ping();
745
746 // Prolong liveness of socket on heartbeat
747 this.removeListener('heartbeat', this.onHeartbeat);
748 this.on('heartbeat', this.onHeartbeat);
749};
750
751/**
752 * Resets ping timeout.
753 *
754 * @api private
755 */
756
757Socket.prototype.onHeartbeat = function (timeout) {
758 clearTimeout(this.pingTimeoutTimer);
759 var self = this;
760 self.pingTimeoutTimer = setTimeout(function () {
761 if ('closed' == self.readyState) return;
762 self.onClose('ping timeout');
763 }, timeout || (self.pingInterval + self.pingTimeout));
764};
765
766/**
767 * Pings server every `this.pingInterval` and expects response
768 * within `this.pingTimeout` or closes connection.
769 *
770 * @api private
771 */
772
773Socket.prototype.ping = function () {
774 var self = this;
775 clearTimeout(self.pingIntervalTimer);
776 self.pingIntervalTimer = setTimeout(function () {
777 debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
778 self.sendPacket('ping');
779 self.onHeartbeat(self.pingTimeout);
780 }, self.pingInterval);
781};
782
783/**
784 * Flush write buffers.
785 *
786 * @api private
787 */
788
789Socket.prototype.flush = function () {
790 if ('closed' != this.readyState && this.transport.writable &&
791 !this.upgrading && this.writeBuffer.length) {
792 debug('flushing %d packets in socket', this.writeBuffer.length);
793 this.transport.send(this.writeBuffer);
794 this.writeBuffer = [];
795 }
796};
797
798/**
799 * Sends a message.
800 *
801 * @param {String} message.
802 * @return {Socket} for chaining.
803 * @api public
804 */
805
806Socket.prototype.write =
807Socket.prototype.send = function (msg) {
808 this.sendPacket('message', msg);
809 return this;
810};
811
812/**
813 * Sends a packet.
814 *
815 * @param {String} packet type.
816 * @param {String} data.
817 * @api private
818 */
819
820Socket.prototype.sendPacket = function (type, data) {
821 var packet = { type: type, data: data };
822 this.emit('packetCreate', packet);
823 this.writeBuffer.push(packet);
824 this.flush();
825};
826
827/**
828 * Closes the connection.
829 *
830 * @api private
831 */
832
833Socket.prototype.close = function () {
834 if ('opening' == this.readyState || 'open' == this.readyState) {
835 this.onClose('forced close');
836 debug('socket closing - telling transport to close');
837 this.transport.close();
838 this.transport.removeAllListeners();
839 }
840
841 return this;
842};
843
844/**
845 * Called upon transport error
846 *
847 * @api private
848 */
849
850Socket.prototype.onError = function (err) {
851 this.emit('error', err);
852 this.onClose('transport error', err);
853};
854
855/**
856 * Called upon transport close.
857 *
858 * @api private
859 */
860
861Socket.prototype.onClose = function (reason, desc) {
862 if ('closed' != this.readyState) {
863 debug('socket close with reason: "%s"', reason);
864 clearTimeout(this.pingIntervalTimer);
865 clearTimeout(this.pingTimeoutTimer);
866 this.readyState = 'closed';
867 this.emit('close', reason, desc);
868 this.onclose && this.onclose.call(this);
869 this.id = null;
870 }
871};
872
873/**
874 * Generates a random uid.
875 *
876 * @api private
877 */
878
879function rnd () {
880 return String(Math.random()).substr(5) + String(Math.random()).substr(5);
881}
882
883});require.register("transport.js", function(module, exports, require, global){
884
885/**
886 * Module dependencies.
887 */
888
889var util = require('./util')
890 , parser = require('./parser')
891 , EventEmitter = require('./event-emitter')
892
893/**
894 * Module exports.
895 */
896
897module.exports = Transport;
898
899/**
900 * Transport abstract constructor.
901 *
902 * @param {Object} options.
903 * @api private
904 */
905
906function Transport (opts) {
907 this.path = opts.path;
908 this.host = opts.host;
909 this.port = opts.port;
910 this.secure = opts.secure;
911 this.query = opts.query;
912 this.timestampParam = opts.timestampParam;
913 this.timestampRequests = opts.timestampRequests;
914 this.readyState = '';
915};
916
917/**
918 * Inherits from EventEmitter.
919 */
920
921util.inherits(Transport, EventEmitter);
922
923/**
924 * Emits an error.
925 *
926 * @param {String} str
927 * @return {Transport} for chaining
928 * @api public
929 */
930
931Transport.prototype.onError = function (msg, desc) {
932 var err = new Error(msg);
933 err.type = 'TransportError';
934 err.description = desc;
935 this.emit('error', err);
936 return this;
937};
938
939/**
940 * Opens the transport.
941 *
942 * @api public
943 */
944
945Transport.prototype.open = function () {
946 if ('closed' == this.readyState || '' == this.readyState) {
947 this.readyState = 'opening';
948 this.doOpen();
949 }
950
951 return this;
952};
953
954/**
955 * Closes the transport.
956 *
957 * @api private
958 */
959
960Transport.prototype.close = function () {
961 if ('opening' == this.readyState || 'open' == this.readyState) {
962 this.doClose();
963 this.onClose();
964 }
965
966 return this;
967};
968
969/**
970 * Sends multiple packets.
971 *
972 * @param {Array} packets
973 * @api private
974 */
975
976Transport.prototype.send = function (packets) {
977 if ('open' == this.readyState) {
978 this.write(packets);
979 } else {
980 throw new Error('Transport not open');
981 }
982}
983
984/**
985 * Called upon open
986 *
987 * @api private
988 */
989
990Transport.prototype.onOpen = function () {
991 this.readyState = 'open';
992 this.writable = true;
993 this.emit('open');
994};
995
996/**
997 * Called with data.
998 *
999 * @param {String} data
1000 * @api private
1001 */
1002
1003Transport.prototype.onData = function (data) {
1004 this.onPacket(parser.decodePacket(data));
1005};
1006
1007/**
1008 * Called with a decoded packet.
1009 */
1010
1011Transport.prototype.onPacket = function (packet) {
1012 this.emit('packet', packet);
1013};
1014
1015/**
1016 * Called upon close.
1017 *
1018 * @api private
1019 */
1020
1021Transport.prototype.onClose = function () {
1022 this.readyState = 'closed';
1023 this.emit('close');
1024};
1025
1026});require.register("transports/flashsocket.js", function(module, exports, require, global){
1027
1028/**
1029 * Module dependencies.
1030 */
1031
1032var WS = require('./websocket')
1033 , util = require('../util');
1034
1035/**
1036 * Module exports.
1037 */
1038
1039module.exports = FlashWS;
1040
1041/**
1042 * Obfuscated key for Blue Coat.
1043 */
1044
1045var xobject = global[['Active'].concat('Object').join('X')];
1046
1047/**
1048 * FlashWS constructor.
1049 *
1050 * @api public
1051 */
1052
1053function FlashWS (options) {
1054 WS.call(this, options);
1055 this.flashPath = options.flashPath;
1056 this.policyPort = options.policyPort;
1057};
1058
1059/**
1060 * Inherits from WebSocket.
1061 */
1062
1063util.inherits(FlashWS, WS);
1064
1065/**
1066 * Transport name.
1067 *
1068 * @api public
1069 */
1070
1071FlashWS.prototype.name = 'flashsocket';
1072
1073/**
1074 * Opens the transport.
1075 *
1076 * @api public
1077 */
1078
1079FlashWS.prototype.doOpen = function () {
1080 if (!this.check()) {
1081 // let the probe timeout
1082 return;
1083 }
1084
1085 // instrument websocketjs logging
1086 function log (type) {
1087 return function () {
1088 var str = Array.prototype.join.call(arguments, ' ');
1089 // debug: [websocketjs %s] %s, type, str
1090 };
1091 };
1092
1093 WEB_SOCKET_LOGGER = { log: log('debug'), error: log('error') };
1094 WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;
1095 WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true;
1096
1097 if ('undefined' == typeof WEB_SOCKET_SWF_LOCATION) {
1098 WEB_SOCKET_SWF_LOCATION = this.flashPath + 'WebSocketMainInsecure.swf';
1099 }
1100
1101 // dependencies
1102 var deps = [this.flashPath + 'web_socket.js'];
1103
1104 if ('undefined' == typeof swfobject) {
1105 deps.unshift(this.flashPath + 'swfobject.js');
1106 }
1107
1108 var self = this;
1109
1110 load(deps, function () {
1111 self.ready(function () {
1112 WebSocket.__addTask(function () {
1113 WS.prototype.doOpen.call(self);
1114 });
1115 });
1116 });
1117};
1118
1119/**
1120 * Override to prevent closing uninitialized flashsocket.
1121 *
1122 * @api private
1123 */
1124
1125FlashWS.prototype.doClose = function () {
1126 if (!this.socket) return;
1127 var self = this;
1128 WebSocket.__addTask(function() {
1129 WS.prototype.doClose.call(self);
1130 });
1131};
1132
1133/**
1134 * Writes to the Flash socket.
1135 *
1136 * @api private
1137 */
1138
1139FlashWS.prototype.write = function() {
1140 var self = this, args = arguments;
1141 WebSocket.__addTask(function () {
1142 WS.prototype.write.apply(self, args);
1143 });
1144};
1145
1146/**
1147 * Called upon dependencies are loaded.
1148 *
1149 * @api private
1150 */
1151
1152FlashWS.prototype.ready = function (fn) {
1153 if (typeof WebSocket == 'undefined' ||
1154 !('__initialize' in WebSocket) || !swfobject) {
1155 return;
1156 }
1157
1158 if (swfobject.getFlashPlayerVersion().major < 10) {
1159 return;
1160 }
1161
1162 function init () {
1163 // Only start downloading the swf file when the checked that this browser
1164 // actually supports it
1165 if (!FlashWS.loaded) {
1166 if (843 != self.policyPort) {
1167 WebSocket.loadFlashPolicyFile('xmlsocket://' + self.host + ':' + self.policyPort);
1168 }
1169
1170 WebSocket.__initialize();
1171 FlashWS.loaded = true;
1172 }
1173
1174 fn.call(self);
1175 }
1176
1177 var self = this;
1178 if (document.body) {
1179 return init();
1180 }
1181
1182 util.load(init);
1183};
1184
1185/**
1186 * Feature detection for flashsocket.
1187 *
1188 * @return {Boolean} whether this transport is available.
1189 * @api public
1190 */
1191
1192FlashWS.prototype.check = function () {
1193
1194
1195
1196
1197 if (typeof WebSocket != 'undefined' && !('__initialize' in WebSocket)) {
1198 return false;
1199 }
1200
1201 if (xobject) {
1202 var control = null;
1203 try {
1204 control = new xobject('ShockwaveFlash.ShockwaveFlash');
1205 } catch (e) { }
1206 if (control) {
1207 return true;
1208 }
1209 } else {
1210 for (var i = 0, l = navigator.plugins.length; i < l; i++) {
1211 for (var j = 0, m = navigator.plugins[i].length; j < m; j++) {
1212 if (navigator.plugins[i][j].description == 'Shockwave Flash') {
1213 return true;
1214 }
1215 }
1216 }
1217 }
1218
1219 return false;
1220};
1221
1222/**
1223 * Lazy loading of scripts.
1224 * Based on $script by Dustin Diaz - MIT
1225 */
1226
1227var scripts = {};
1228
1229/**
1230 * Injects a script. Keeps tracked of injected ones.
1231 *
1232 * @param {String} path
1233 * @param {Function} callback
1234 * @api private
1235 */
1236
1237function create (path, fn) {
1238 if (scripts[path]) return fn();
1239
1240 var el = document.createElement('script');
1241 var loaded = false;
1242
1243 // debug: loading "%s", path
1244 el.onload = el.onreadystatechange = function () {
1245 if (loaded || scripts[path]) return;
1246 var rs = el.readyState;
1247 if (!rs || 'loaded' == rs || 'complete' == rs) {
1248 // debug: loaded "%s", path
1249 el.onload = el.onreadystatechange = null;
1250 loaded = true;
1251 scripts[path] = true;
1252 fn();
1253 }
1254 };
1255
1256 el.async = 1;
1257 el.src = path;
1258
1259 var head = document.getElementsByTagName('head')[0];
1260 head.insertBefore(el, head.firstChild);
1261};
1262
1263/**
1264 * Loads scripts and fires a callback.
1265 *
1266 * @param {Array} paths
1267 * @param {Function} callback
1268 */
1269
1270function load (arr, fn) {
1271 function process (i) {
1272 if (!arr[i]) return fn();
1273 create(arr[i], function () {
1274 process(++i);
1275 });
1276 };
1277
1278 process(0);
1279};
1280
1281});require.register("transports/index.js", function(module, exports, require, global){
1282
1283/**
1284 * Module dependencies
1285 */
1286
1287var XHR = require('./polling-xhr')
1288 , JSONP = require('./polling-jsonp')
1289 , websocket = require('./websocket')
1290 , flashsocket = require('./flashsocket')
1291 , util = require('../util');
1292
1293/**
1294 * Export transports.
1295 */
1296
1297exports.polling = polling;
1298exports.websocket = websocket;
1299exports.flashsocket = flashsocket;
1300
1301/**
1302 * Polling transport polymorphic constructor.
1303 * Decides on xhr vs jsonp based on feature detection.
1304 *
1305 * @api private
1306 */
1307
1308function polling (opts) {
1309 var xhr
1310 , xd = false
1311 , isXProtocol = false;
1312
1313 if (global.location) {
1314 var isSSL = 'https:' == location.protocol;
1315 var port = location.port;
1316
1317 // some user agents have empty `location.port`
1318 if (Number(port) != port) {
1319 port = isSSL ? 443 : 80;
1320 }
1321
1322 xd = opts.host != location.hostname || port != opts.port;
1323 isXProtocol = opts.secure != isSSL;
1324 }
1325
1326 xhr = util.request(xd);
1327 /* See #7 at http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx */
1328 if (isXProtocol && global.XDomainRequest && xhr instanceof global.XDomainRequest) {
1329 return new JSONP(opts);
1330 }
1331
1332 if (xhr && !opts.forceJSONP) {
1333 return new XHR(opts);
1334 } else {
1335 return new JSONP(opts);
1336 }
1337};
1338
1339});require.register("transports/polling-jsonp.js", function(module, exports, require, global){
1340
1341/**
1342 * Module requirements.
1343 */
1344
1345var Polling = require('./polling')
1346 , util = require('../util');
1347
1348/**
1349 * Module exports.
1350 */
1351
1352module.exports = JSONPPolling;
1353
1354/**
1355 * Cached regular expressions.
1356 */
1357
1358var rNewline = /\n/g;
1359
1360/**
1361 * Global JSONP callbacks.
1362 */
1363
1364var callbacks;
1365
1366/**
1367 * Callbacks count.
1368 */
1369
1370var index = 0;
1371
1372/**
1373 * Noop.
1374 */
1375
1376function empty () { }
1377
1378/**
1379 * JSONP Polling constructor.
1380 *
1381 * @param {Object} opts.
1382 * @api public
1383 */
1384
1385function JSONPPolling (opts) {
1386 Polling.call(this, opts);
1387
1388 // define global callbacks array if not present
1389 // we do this here (lazily) to avoid unneeded global pollution
1390 if (!callbacks) {
1391 // we need to consider multiple engines in the same page
1392 if (!global.___eio) global.___eio = [];
1393 callbacks = global.___eio;
1394 }
1395
1396 // callback identifier
1397 this.index = callbacks.length;
1398
1399 // add callback to jsonp global
1400 var self = this;
1401 callbacks.push(function (msg) {
1402 self.onData(msg);
1403 });
1404
1405 // append to query string
1406 this.query.j = this.index;
1407};
1408
1409/**
1410 * Inherits from Polling.
1411 */
1412
1413util.inherits(JSONPPolling, Polling);
1414
1415/**
1416 * Opens the socket.
1417 *
1418 * @api private
1419 */
1420
1421JSONPPolling.prototype.doOpen = function () {
1422 var self = this;
1423 util.defer(function () {
1424 Polling.prototype.doOpen.call(self);
1425 });
1426};
1427
1428/**
1429 * Closes the socket
1430 *
1431 * @api private
1432 */
1433
1434JSONPPolling.prototype.doClose = function () {
1435 if (this.script) {
1436 this.script.parentNode.removeChild(this.script);
1437 this.script = null;
1438 }
1439
1440 if (this.form) {
1441 this.form.parentNode.removeChild(this.form);
1442 this.form = null;
1443 }
1444
1445 Polling.prototype.doClose.call(this);
1446};
1447
1448/**
1449 * Starts a poll cycle.
1450 *
1451 * @api private
1452 */
1453
1454JSONPPolling.prototype.doPoll = function () {
1455 var script = document.createElement('script');
1456
1457 if (this.script) {
1458 this.script.parentNode.removeChild(this.script);
1459 this.script = null;
1460 }
1461
1462 script.async = true;
1463 script.src = this.uri();
1464
1465 var insertAt = document.getElementsByTagName('script')[0];
1466 insertAt.parentNode.insertBefore(script, insertAt);
1467 this.script = script;
1468
1469 if (util.ua.gecko) {
1470 setTimeout(function () {
1471 var iframe = document.createElement('iframe');
1472 document.body.appendChild(iframe);
1473 document.body.removeChild(iframe);
1474 }, 100);
1475 }
1476};
1477
1478/**
1479 * Writes with a hidden iframe.
1480 *
1481 * @param {String} data to send
1482 * @param {Function} called upon flush.
1483 * @api private
1484 */
1485
1486JSONPPolling.prototype.doWrite = function (data, fn) {
1487 var self = this;
1488
1489 if (!this.form) {
1490 var form = document.createElement('form')
1491 , area = document.createElement('textarea')
1492 , id = this.iframeId = 'eio_iframe_' + this.index
1493 , iframe;
1494
1495 form.className = 'socketio';
1496 form.style.position = 'absolute';
1497 form.style.top = '-1000px';
1498 form.style.left = '-1000px';
1499 form.target = id;
1500 form.method = 'POST';
1501 form.setAttribute('accept-charset', 'utf-8');
1502 area.name = 'd';
1503 form.appendChild(area);
1504 document.body.appendChild(form);
1505
1506 this.form = form;
1507 this.area = area;
1508 }
1509
1510 this.form.action = this.uri();
1511
1512 function complete () {
1513 initIframe();
1514 fn();
1515 };
1516
1517 function initIframe () {
1518 if (self.iframe) {
1519 self.form.removeChild(self.iframe);
1520 }
1521
1522 try {
1523 // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
1524 iframe = document.createElement('<iframe name="'+ self.iframeId +'">');
1525 } catch (e) {
1526 iframe = document.createElement('iframe');
1527 iframe.name = self.iframeId;
1528 }
1529
1530 iframe.id = self.iframeId;
1531
1532 self.form.appendChild(iframe);
1533 self.iframe = iframe;
1534 };
1535
1536 initIframe();
1537
1538 // escape \n to prevent it from being converted into \r\n by some UAs
1539 this.area.value = data.replace(rNewline, '\\n');
1540
1541 try {
1542 this.form.submit();
1543 } catch(e) {}
1544
1545 if (this.iframe.attachEvent) {
1546 this.iframe.onreadystatechange = function(){
1547 if (self.iframe.readyState == 'complete') {
1548 complete();
1549 }
1550 };
1551 } else {
1552 this.iframe.onload = complete;
1553 }
1554};
1555
1556});require.register("transports/polling-xhr.js", function(module, exports, require, global){
1557/**
1558 * Module requirements.
1559 */
1560
1561var Polling = require('./polling')
1562 , EventEmitter = require('../event-emitter')
1563 , util = require('../util');
1564
1565/**
1566 * Module exports.
1567 */
1568
1569module.exports = XHR;
1570module.exports.Request = Request;
1571
1572/**
1573 * Obfuscated key for Blue Coat.
1574 */
1575
1576var xobject = global[['Active'].concat('Object').join('X')];
1577
1578/**
1579 * Empty function
1580 */
1581
1582function empty () { }
1583
1584/**
1585 * XHR Polling constructor.
1586 *
1587 * @param {Object} opts
1588 * @api public
1589 */
1590
1591function XHR (opts) {
1592 Polling.call(this, opts);
1593
1594 if (global.location) {
1595 this.xd = opts.host != global.location.hostname ||
1596 global.location.port != opts.port;
1597 }
1598};
1599
1600/**
1601 * Inherits from Polling.
1602 */
1603
1604util.inherits(XHR, Polling);
1605
1606/**
1607 * Opens the socket
1608 *
1609 * @api private
1610 */
1611
1612XHR.prototype.doOpen = function () {
1613 var self = this;
1614 util.defer(function () {
1615 Polling.prototype.doOpen.call(self);
1616 });
1617};
1618
1619/**
1620 * Creates a request.
1621 *
1622 * @param {String} method
1623 * @api private
1624 */
1625
1626XHR.prototype.request = function (opts) {
1627 opts = opts || {};
1628 opts.uri = this.uri();
1629 opts.xd = this.xd;
1630 return new Request(opts);
1631};
1632
1633/**
1634 * Sends data.
1635 *
1636 * @param {String} data to send.
1637 * @param {Function} called upon flush.
1638 * @api private
1639 */
1640
1641XHR.prototype.doWrite = function (data, fn) {
1642 var req = this.request({ method: 'POST', data: data });
1643 var self = this;
1644 req.on('success', fn);
1645 req.on('error', function (err) {
1646 self.onError('xhr post error', err);
1647 });
1648 this.sendXhr = req;
1649};
1650
1651/**
1652 * Starts a poll cycle.
1653 *
1654 * @api private
1655 */
1656
1657XHR.prototype.doPoll = function () {
1658 // debug: xhr poll
1659 var req = this.request();
1660 var self = this;
1661 req.on('data', function (data) {
1662 self.onData(data);
1663 });
1664 req.on('error', function (err) {
1665 self.onError('xhr poll error', err);
1666 });
1667 this.pollXhr = req;
1668};
1669
1670/**
1671 * Request constructor
1672 *
1673 * @param {Object} options
1674 * @api public
1675 */
1676
1677function Request (opts) {
1678 this.method = opts.method || 'GET';
1679 this.uri = opts.uri;
1680 this.xd = !!opts.xd;
1681 this.async = false !== opts.async;
1682 this.data = undefined != opts.data ? opts.data : null;
1683 this.create();
1684}
1685
1686/**
1687 * Inherits from Polling.
1688 */
1689
1690util.inherits(Request, EventEmitter);
1691
1692/**
1693 * Creates the XHR object and sends the request.
1694 *
1695 * @api private
1696 */
1697
1698Request.prototype.create = function () {
1699 var xhr = this.xhr = util.request(this.xd);
1700 var self = this;
1701
1702 xhr.open(this.method, this.uri, this.async);
1703
1704 if ('POST' == this.method) {
1705 try {
1706 if (xhr.setRequestHeader) {
1707 // xmlhttprequest
1708 xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
1709 } else {
1710 // xdomainrequest
1711 xhr.contentType = 'text/plain';
1712 }
1713 } catch (e) {}
1714 }
1715
1716 if (this.xd && global.XDomainRequest && xhr instanceof XDomainRequest) {
1717 xhr.onerror = function (e) {
1718 self.onError(e);
1719 };
1720 xhr.onload = function () {
1721 self.onData(xhr.responseText);
1722 };
1723 xhr.onprogress = empty;
1724 } else {
1725 // ie6 check
1726 if ('withCredentials' in xhr) {
1727 xhr.withCredentials = true;
1728 }
1729
1730 xhr.onreadystatechange = function () {
1731 var data;
1732
1733 try {
1734 if (4 != xhr.readyState) return;
1735 if (200 == xhr.status || 1223 == xhr.status) {
1736 data = xhr.responseText;
1737 } else {
1738 self.onError(xhr.status);
1739 }
1740 } catch (e) {
1741 self.onError(e);
1742 }
1743
1744 if (undefined !== data) {
1745 self.onData(data);
1746 }
1747 };
1748 }
1749
1750 // debug: sending xhr with url %s | data %s, this.uri, this.data
1751 xhr.send(this.data);
1752
1753 if (xobject) {
1754 this.index = Request.requestsCount++;
1755 Request.requests[this.index] = this;
1756 }
1757};
1758
1759/**
1760 * Called upon successful response.
1761 *
1762 * @api private
1763 */
1764
1765Request.prototype.onSuccess = function () {
1766 this.emit('success');
1767 this.cleanup();
1768};
1769
1770/**
1771 * Called if we have data.
1772 *
1773 * @api private
1774 */
1775
1776Request.prototype.onData = function (data) {
1777 this.emit('data', data);
1778 this.onSuccess();
1779};
1780
1781/**
1782 * Called upon error.
1783 *
1784 * @api private
1785 */
1786
1787Request.prototype.onError = function (err) {
1788 this.emit('error', err);
1789 this.cleanup();
1790};
1791
1792/**
1793 * Cleans up house.
1794 *
1795 * @api private
1796 */
1797
1798Request.prototype.cleanup = function () {
1799 // xmlhttprequest
1800 this.xhr.onreadystatechange = empty;
1801
1802 // xdomainrequest
1803 this.xhr.onload = this.xhr.onerror = empty;
1804
1805 try {
1806 this.xhr.abort();
1807 } catch(e) {}
1808
1809 if (xobject) {
1810 delete Request.requests[this.index];
1811 }
1812
1813 this.xhr = null;
1814};
1815
1816/**
1817 * Aborts the request.
1818 *
1819 * @api public
1820 */
1821
1822Request.prototype.abort = function () {
1823 this.cleanup();
1824};
1825
1826if (xobject) {
1827 Request.requestsCount = 0;
1828 Request.requests = {};
1829
1830 global.attachEvent('onunload', function () {
1831 for (var i in Request.requests) {
1832 if (Request.requests.hasOwnProperty(i)) {
1833 Request.requests[i].abort();
1834 }
1835 }
1836 });
1837}
1838
1839});require.register("transports/polling.js", function(module, exports, require, global){
1840/**
1841 * Module dependencies.
1842 */
1843
1844var Transport = require('../transport')
1845 , util = require('../util')
1846 , parser = require('../parser')
1847
1848/**
1849 * Module exports.
1850 */
1851
1852module.exports = Polling;
1853
1854/**
1855 * Polling interface.
1856 *
1857 * @param {Object} opts
1858 * @api private
1859 */
1860
1861function Polling (opts) {
1862 Transport.call(this, opts);
1863}
1864
1865/**
1866 * Inherits from Transport.
1867 */
1868
1869util.inherits(Polling, Transport);
1870
1871/**
1872 * Transport name.
1873 */
1874
1875Polling.prototype.name = 'polling';
1876
1877/**
1878 * Opens the socket (triggers polling). We write a PING message to determine
1879 * when the transport is open.
1880 *
1881 * @api private
1882 */
1883
1884Polling.prototype.doOpen = function () {
1885 this.poll();
1886};
1887
1888/**
1889 * Pauses polling.
1890 *
1891 * @param {Function} callback upon buffers are flushed and transport is paused
1892 * @api private
1893 */
1894
1895Polling.prototype.pause = function (onPause) {
1896 var pending = 0
1897 , self = this
1898
1899 this.readyState = 'pausing';
1900
1901 function pause () {
1902 // debug: paused
1903 self.readyState = 'paused';
1904 onPause();
1905 }
1906
1907 if (this.polling || !this.writable) {
1908 var total = 0;
1909
1910 if (this.polling) {
1911 // debug: we are currently polling - waiting to pause
1912 total++;
1913 this.once('pollComplete', function () {
1914 // debug: pre-pause polling complete
1915 --total || pause();
1916 });
1917 }
1918
1919 if (!this.writable) {
1920 // debug: we are currently writing - waiting to pause
1921 total++;
1922 this.once('drain', function () {
1923 // debug: pre-pause writing complete
1924 --total || pause();
1925 });
1926 }
1927 } else {
1928 pause();
1929 }
1930};
1931
1932/**
1933 * Starts polling cycle.
1934 *
1935 * @api public
1936 */
1937
1938Polling.prototype.poll = function () {
1939 // debug: polling
1940 this.polling = true;
1941 this.doPoll();
1942 this.emit('poll');
1943};
1944
1945/**
1946 * Overloads onData to detect payloads.
1947 *
1948 * @api private
1949 */
1950
1951Polling.prototype.onData = function (data) {
1952 // debug: polling got, data
1953 // decode payload
1954 var packets = parser.decodePayload(data);
1955
1956 for (var i = 0, l = packets.length; i < l; i++) {
1957 // if its the first message we consider the trnasport open
1958 if ('opening' == this.readyState) {
1959 this.onOpen();
1960 }
1961
1962 // if its a close packet, we close the ongoing requests
1963 if ('close' == packets[i].type) {
1964 this.onClose();
1965 return;
1966 }
1967
1968 // otherwise bypass onData and handle the message
1969 this.onPacket(packets[i]);
1970 }
1971
1972 // if we got data we're not polling
1973 this.polling = false;
1974 this.emit('pollComplete');
1975
1976 if ('open' == this.readyState) {
1977 this.poll();
1978 } else {
1979 // debug: ignoring poll - transport state "%s", this.readyState
1980 }
1981};
1982
1983/**
1984 * For polling, send a close packet.
1985 *
1986 * @api private
1987 */
1988
1989Polling.prototype.doClose = function () {
1990 // debug: sending close packet
1991 this.send([{ type: 'close' }]);
1992};
1993
1994/**
1995 * Writes a packets payload.
1996 *
1997 * @param {Array} data packets
1998 * @param {Function} drain callback
1999 * @api private
2000 */
2001
2002Polling.prototype.write = function (packets) {
2003 var self = this;
2004 this.writable = false;
2005 this.doWrite(parser.encodePayload(packets), function () {
2006 self.writable = true;
2007 self.emit('drain');
2008 });
2009};
2010
2011/**
2012 * Generates uri for connection.
2013 *
2014 * @api private
2015 */
2016
2017Polling.prototype.uri = function () {
2018 var query = this.query || {}
2019 , schema = this.secure ? 'https' : 'http'
2020 , port = ''
2021
2022 // cache busting is forced for IE / android / iOS6 ಠ_ಠ
2023 if (global.ActiveXObject || util.ua.android || util.ua.ios6
2024 || this.timestampRequests) {
2025 query[this.timestampParam] = +new Date;
2026 }
2027
2028 query = util.qs(query);
2029
2030 // avoid port if default for schema
2031 if (this.port && (('https' == schema && this.port != 443)
2032 || ('http' == schema && this.port != 80))) {
2033 port = ':' + this.port;
2034 }
2035
2036 // prepend ? to query
2037 if (query.length) {
2038 query = '?' + query;
2039 }
2040
2041 return schema + '://' + this.host + port + this.path + query;
2042};
2043
2044});require.register("transports/websocket.js", function(module, exports, require, global){
2045
2046/**
2047 * Module dependencies.
2048 */
2049
2050var Transport = require('../transport')
2051 , parser = require('../parser')
2052 , util = require('../util')
2053
2054/**
2055 * Module exports.
2056 */
2057
2058module.exports = WS;
2059
2060/**
2061 * WebSocket transport constructor.
2062 *
2063 * @api {Object} connection options
2064 * @api public
2065 */
2066
2067function WS (opts) {
2068 Transport.call(this, opts);
2069};
2070
2071/**
2072 * Inherits from Transport.
2073 */
2074
2075util.inherits(WS, Transport);
2076
2077/**
2078 * Transport name.
2079 *
2080 * @api public
2081 */
2082
2083WS.prototype.name = 'websocket';
2084
2085/**
2086 * Opens socket.
2087 *
2088 * @api private
2089 */
2090
2091WS.prototype.doOpen = function () {
2092 if (!this.check()) {
2093 // let probe timeout
2094 return;
2095 }
2096
2097 var self = this;
2098
2099 this.socket = new (ws())(this.uri());
2100 this.socket.onopen = function () {
2101 self.onOpen();
2102 };
2103 this.socket.onclose = function () {
2104 self.onClose();
2105 };
2106 this.socket.onmessage = function (ev) {
2107 self.onData(ev.data);
2108 };
2109 this.socket.onerror = function (e) {
2110 self.onError('websocket error', e);
2111 };
2112};
2113
2114/**
2115 * Writes data to socket.
2116 *
2117 * @param {Array} array of packets.
2118 * @api private
2119 */
2120
2121WS.prototype.write = function (packets) {
2122 for (var i = 0, l = packets.length; i < l; i++) {
2123 this.socket.send(parser.encodePacket(packets[i]));
2124 }
2125};
2126
2127/**
2128 * Closes socket.
2129 *
2130 * @api private
2131 */
2132
2133WS.prototype.doClose = function () {
2134 if (typeof this.socket !== 'undefined') {
2135 this.socket.close();
2136 }
2137};
2138
2139/**
2140 * Generates uri for connection.
2141 *
2142 * @api private
2143 */
2144
2145WS.prototype.uri = function () {
2146 var query = this.query || {}
2147 , schema = this.secure ? 'wss' : 'ws'
2148 , port = ''
2149
2150 // avoid port if default for schema
2151 if (this.port && (('wss' == schema && this.port != 443)
2152 || ('ws' == schema && this.port != 80))) {
2153 port = ':' + this.port;
2154 }
2155
2156 // append timestamp to URI
2157 if (this.timestampRequests) {
2158 query[this.timestampParam] = +new Date;
2159 }
2160
2161 query = util.qs(query);
2162
2163 // prepend ? to query
2164 if (query.length) {
2165 query = '?' + query;
2166 }
2167
2168 return schema + '://' + this.host + port + this.path + query;
2169};
2170
2171/**
2172 * Feature detection for WebSocket.
2173 *
2174 * @return {Boolean} whether this transport is available.
2175 * @api public
2176 */
2177
2178WS.prototype.check = function () {
2179 var websocket = ws();
2180 return !!websocket && !('__initialize' in websocket && this.name === WS.prototype.name);
2181}
2182
2183/**
2184 * Getter for WS constructor.
2185 *
2186 * @api private
2187 */
2188
2189function ws () {
2190
2191
2192
2193
2194 return global.WebSocket || global.MozWebSocket;
2195}
2196
2197});require.register("util.js", function(module, exports, require, global){
2198
2199/**
2200 * Status of page load.
2201 */
2202
2203var pageLoaded = false;
2204
2205/**
2206 * Inheritance.
2207 *
2208 * @param {Function} ctor a
2209 * @param {Function} ctor b
2210 * @api private
2211 */
2212
2213exports.inherits = function inherits (a, b) {
2214 function c () { }
2215 c.prototype = b.prototype;
2216 a.prototype = new c;
2217};
2218
2219/**
2220 * Object.keys
2221 */
2222
2223exports.keys = Object.keys || function (obj) {
2224 var ret = []
2225 , has = Object.prototype.hasOwnProperty
2226
2227 for (var i in obj) {
2228 if (has.call(obj, i)) {
2229 ret.push(i);
2230 }
2231 }
2232
2233 return ret;
2234};
2235
2236/**
2237 * Adds an event.
2238 *
2239 * @api private
2240 */
2241
2242exports.on = function (element, event, fn, capture) {
2243 if (element.attachEvent) {
2244 element.attachEvent('on' + event, fn);
2245 } else if (element.addEventListener) {
2246 element.addEventListener(event, fn, capture);
2247 }
2248};
2249
2250/**
2251 * Load utility.
2252 *
2253 * @api private
2254 */
2255
2256exports.load = function (fn) {
2257 if (global.document && document.readyState === 'complete' || pageLoaded) {
2258 return fn();
2259 }
2260
2261 exports.on(global, 'load', fn, false);
2262};
2263
2264/**
2265 * Change the internal pageLoaded value.
2266 */
2267
2268if ('undefined' != typeof window) {
2269 exports.load(function () {
2270 pageLoaded = true;
2271 });
2272}
2273
2274/**
2275 * Defers a function to ensure a spinner is not displayed by the browser.
2276 *
2277 * @param {Function} fn
2278 * @api private
2279 */
2280
2281exports.defer = function (fn) {
2282 if (!exports.ua.webkit || 'undefined' != typeof importScripts) {
2283 return fn();
2284 }
2285
2286 exports.load(function () {
2287 setTimeout(fn, 100);
2288 });
2289};
2290
2291/**
2292 * JSON parse.
2293 *
2294 * @see Based on jQuery#parseJSON (MIT) and JSON2
2295 * @api private
2296 */
2297
2298var rvalidchars = /^[\],:{}\s]*$/
2299 , rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g
2300 , rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g
2301 , rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g
2302 , rtrimLeft = /^\s+/
2303 , rtrimRight = /\s+$/
2304
2305exports.parseJSON = function (data) {
2306 if ('string' != typeof data || !data) {
2307 return null;
2308 }
2309
2310 data = data.replace(rtrimLeft, '').replace(rtrimRight, '');
2311
2312 // Attempt to parse using the native JSON parser first
2313 if (global.JSON && JSON.parse) {
2314 return JSON.parse(data);
2315 }
2316
2317 if (rvalidchars.test(data.replace(rvalidescape, '@')
2318 .replace(rvalidtokens, ']')
2319 .replace(rvalidbraces, ''))) {
2320 return (new Function('return ' + data))();
2321 }
2322};
2323
2324/**
2325 * UA / engines detection namespace.
2326 *
2327 * @namespace
2328 */
2329
2330exports.ua = {};
2331
2332/**
2333 * Whether the UA supports CORS for XHR.
2334 *
2335 * @api private
2336 */
2337
2338exports.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () {
2339 try {
2340 var a = new XMLHttpRequest();
2341 } catch (e) {
2342 return false;
2343 }
2344
2345 return a.withCredentials != undefined;
2346})();
2347
2348/**
2349 * Detect webkit.
2350 *
2351 * @api private
2352 */
2353
2354exports.ua.webkit = 'undefined' != typeof navigator &&
2355 /webkit/i.test(navigator.userAgent);
2356
2357/**
2358 * Detect gecko.
2359 *
2360 * @api private
2361 */
2362
2363exports.ua.gecko = 'undefined' != typeof navigator &&
2364 /gecko/i.test(navigator.userAgent);
2365
2366/**
2367 * Detect android;
2368 */
2369
2370exports.ua.android = 'undefined' != typeof navigator &&
2371 /android/i.test(navigator.userAgent);
2372
2373/**
2374 * Detect iOS.
2375 */
2376
2377exports.ua.ios = 'undefined' != typeof navigator &&
2378 /^(iPad|iPhone|iPod)$/.test(navigator.platform);
2379exports.ua.ios6 = exports.ua.ios && /OS 6_/.test(navigator.userAgent);
2380
2381/**
2382 * XHR request helper.
2383 *
2384 * @param {Boolean} whether we need xdomain
2385 * @api private
2386 */
2387
2388exports.request = function request (xdomain) {
2389
2390
2391
2392
2393
2394 if (xdomain && 'undefined' != typeof XDomainRequest && !exports.ua.hasCORS) {
2395 return new XDomainRequest();
2396 }
2397
2398 // XMLHttpRequest can be disabled on IE
2399 try {
2400 if ('undefined' != typeof XMLHttpRequest && (!xdomain || exports.ua.hasCORS)) {
2401 return new XMLHttpRequest();
2402 }
2403 } catch (e) { }
2404
2405 if (!xdomain) {
2406 try {
2407 return new ActiveXObject('Microsoft.XMLHTTP');
2408 } catch(e) { }
2409 }
2410};
2411
2412/**
2413 * Parses an URI
2414 *
2415 * @author Steven Levithan <stevenlevithan.com> (MIT license)
2416 * @api private
2417 */
2418
2419var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
2420
2421var parts = [
2422 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host'
2423 , 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
2424];
2425
2426exports.parseUri = function (str) {
2427 var m = re.exec(str || '')
2428 , uri = {}
2429 , i = 14;
2430
2431 while (i--) {
2432 uri[parts[i]] = m[i] || '';
2433 }
2434
2435 return uri;
2436};
2437
2438/**
2439 * Compiles a querystring
2440 *
2441 * @param {Object}
2442 * @api private
2443 */
2444
2445exports.qs = function (obj) {
2446 var str = '';
2447
2448 for (var i in obj) {
2449 if (obj.hasOwnProperty(i)) {
2450 if (str.length) str += '&';
2451 str += i + '=' + encodeURIComponent(obj[i]);
2452 }
2453 }
2454
2455 return str;
2456};
2457
2458});var exp = require('engine.io-client');if ("undefined" != typeof module) module.exports = exp;else eio = exp;
2459})();
2460// Generated by CoffeeScript 1.4.0
2461(function() {
2462 var Call, EventEmitter, IceCandidate, PeerConnection, RTC, SessionDescription, URL, getUserMedia, holla,
2463 __hasProp = {}.hasOwnProperty,
2464 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
2465 __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
2466
2467 EventEmitter = eio.EventEmitter;
2468
2469 PeerConnection = window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection;
2470
2471 IceCandidate = window.RTCIceCandidate;
2472
2473 SessionDescription = window.RTCSessionDescription;
2474
2475 URL = window.URL || window.webkitURL || window.msURL || window.oURL;
2476
2477 getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
2478
2479 RTC = (function(_super) {
2480
2481 __extends(RTC, _super);
2482
2483 function RTC(opts) {
2484 var _ref, _ref1, _ref2, _ref3,
2485 _this = this;
2486 if (opts == null) {
2487 opts = {};
2488 }
2489 if ((_ref = opts.host) == null) {
2490 opts.host = window.location.hostname;
2491 }
2492 if ((_ref1 = opts.port) == null) {
2493 opts.port = (window.location.port.length > 0 ? parseInt(window.location.port) : 80);
2494 }
2495 if ((_ref2 = opts.secure) == null) {
2496 opts.secure = window.location.protocol === 'https:';
2497 }
2498 if ((_ref3 = opts.path) == null) {
2499 opts.path = "/holla";
2500 }
2501 this.socket = new eio.Socket(opts);
2502 this.socket.on("open", this.emit.bind("connected"));
2503 this.socket.on("close", this.emit.bind("disconnected"));
2504 this.socket.on("error", this.emit.bind("error"));
2505 this.socket.on("message", function(msg) {
2506 var c;
2507 msg = JSON.parse(msg);
2508 if (msg.type === "presence") {
2509 _this.emit("presence", msg.args);
2510 _this.emit("presence." + msg.args.name, msg.args.online);
2511 return;
2512 }
2513 if (msg.type !== "offer") {
2514 return;
2515 }
2516 c = new Call(_this, msg.from, false);
2517 _this.emit("call", c);
2518 });
2519 }
2520
2521 RTC.prototype.register = function(name, cb) {
2522 var handle,
2523 _this = this;
2524 this.socket.send(JSON.stringify({
2525 type: "register",
2526 args: {
2527 name: name
2528 }
2529 }));
2530 handle = function(msg) {
2531 msg = JSON.parse(msg);
2532 if (msg.type !== "register") {
2533 return;
2534 }
2535 _this.socket.removeListener("message", handle);
2536 if (msg.args.result === true) {
2537 _this.user = name;
2538 _this.authorized = true;
2539 _this.emit("authorized");
2540 }
2541 return typeof cb === "function" ? cb(msg.args.result) : void 0;
2542 };
2543 return this.socket.on("message", handle);
2544 };
2545
2546 RTC.prototype.call = function(user) {
2547 return new Call(this, user, true);
2548 };
2549
2550 RTC.prototype.ready = function(fn) {
2551 if (this.authorized) {
2552 return fn();
2553 } else {
2554 return this.once('authorized', fn);
2555 }
2556 };
2557
2558 return RTC;
2559
2560 })(EventEmitter);
2561
2562 Call = (function(_super) {
2563
2564 __extends(Call, _super);
2565
2566 function Call(parent, user, isCaller) {
2567 this.parent = parent;
2568 this.user = user;
2569 this.isCaller = isCaller;
2570 this.handleMessage = __bind(this.handleMessage, this);
2571
2572 this.startTime = new Date;
2573 this.socket = this.parent.socket;
2574 this.pc = this.createConnection();
2575 if (this.isCaller) {
2576 this.socket.send(JSON.stringify({
2577 type: "offer",
2578 to: this.user
2579 }));
2580 }
2581 this.emit("calling");
2582 this.socket.on("message", this.handleMessage);
2583 }
2584
2585 Call.prototype.createConnection = function() {
2586 var pc,
2587 _this = this;
2588 pc = new PeerConnection(holla.config);
2589 pc.onconnecting = function() {
2590 _this.emit('connecting');
2591 };
2592 pc.onopen = function() {
2593 _this.emit('connected');
2594 };
2595 pc.onicecandidate = function(evt) {
2596 if (evt.candidate) {
2597 _this.socket.send(JSON.stringify({
2598 type: "candidate",
2599 to: _this.user,
2600 args: {
2601 candidate: evt.candidate
2602 }
2603 }));
2604 }
2605 };
2606 pc.onaddstream = function(evt) {
2607 _this.remoteStream = evt.stream;
2608 _this._ready = true;
2609 _this.emit("ready", _this.remoteStream);
2610 };
2611 pc.onremovestream = function(evt) {
2612 console.log(evt);
2613 };
2614 return pc;
2615 };
2616
2617 Call.prototype.handleMessage = function(msg) {
2618 msg = JSON.parse(msg);
2619 if (msg.from !== this.user) {
2620 return;
2621 }
2622 if (msg.type === "answer") {
2623 if (!msg.args.accepted) {
2624 return this.emit("rejected");
2625 }
2626 this.emit("answered");
2627 this.initSDP();
2628 } else if (msg.type === "candidate") {
2629 this.pc.addIceCandidate(new IceCandidate(msg.args.candidate));
2630 } else if (msg.type === "sdp") {
2631 this.pc.setRemoteDescription(new SessionDescription(msg.args));
2632 this.emit("sdp");
2633 } else if (msg.type === "hangup") {
2634 this.emit("hangup");
2635 } else if (msg.type === "chat") {
2636 this.emit("chat", msg.args.message);
2637 }
2638 };
2639
2640 Call.prototype.addStream = function(s) {
2641 this.pc.addStream(s);
2642 return this;
2643 };
2644
2645 Call.prototype.ready = function(fn) {
2646 if (this._ready) {
2647 fn(this.remoteStream);
2648 } else {
2649 this.once('ready', fn);
2650 }
2651 return this;
2652 };
2653
2654 Call.prototype.duration = function() {
2655 var e, s;
2656 if (this.endTime != null) {
2657 s = this.endTime.getTime();
2658 }
2659 if (s == null) {
2660 s = Date.now();
2661 }
2662 e = this.startTime.getTime();
2663 return (s - e) / 1000;
2664 };
2665
2666 Call.prototype.chat = function(msg) {
2667 this.socket.send(JSON.stringify({
2668 type: "chat",
2669 to: this.user,
2670 args: {
2671 message: msg
2672 }
2673 }));
2674 return this;
2675 };
2676
2677 Call.prototype.answer = function() {
2678 this.startTime = new Date;
2679 this.socket.send(JSON.stringify({
2680 type: "answer",
2681 to: this.user,
2682 args: {
2683 accepted: true
2684 }
2685 }));
2686 this.initSDP();
2687 return this;
2688 };
2689
2690 Call.prototype.decline = function() {
2691 this.socket.send(JSON.stringify({
2692 type: "answer",
2693 to: this.user,
2694 args: {
2695 accepted: false
2696 }
2697 }));
2698 return this;
2699 };
2700
2701 Call.prototype.end = function() {
2702 this.endTime = new Date;
2703 this.pc.close();
2704 this.socket.send(JSON.stringify({
2705 type: "hangup",
2706 to: this.user
2707 }));
2708 this.emit("hangup");
2709 return this;
2710 };
2711
2712 Call.prototype.initSDP = function() {
2713 var done, err,
2714 _this = this;
2715 done = function(desc) {
2716 _this.pc.setLocalDescription(desc);
2717 return _this.socket.send(JSON.stringify({
2718 type: "sdp",
2719 to: _this.user,
2720 args: desc
2721 }));
2722 };
2723 err = function(e) {
2724 return console.log(e);
2725 };
2726 if (this.isCaller) {
2727 return this.pc.createOffer(done, err);
2728 }
2729 if (this.pc.remoteDescription) {
2730 return this.pc.createAnswer(done, err);
2731 }
2732 return this.once("sdp", function() {
2733 return _this.pc.createAnswer(done, err);
2734 });
2735 };
2736
2737 return Call;
2738
2739 })(EventEmitter);
2740
2741 holla = {
2742 Call: Call,
2743 RTC: RTC,
2744 supported: (PeerConnection != null) && (getUserMedia != null),
2745 connect: function(host) {
2746 return new RTC(host);
2747 },
2748 config: {
2749 iceServers: [
2750 {
2751 url: "stun:stun.l.google.com:19302"
2752 }
2753 ]
2754 },
2755 streamToBlob: function(s) {
2756 return URL.createObjectURL(s);
2757 },
2758 pipe: function(stream, el) {
2759 var uri;
2760 uri = holla.streamToBlob(stream);
2761 if (typeof el === "string") {
2762 document.getElementById(el).src;
2763 } else if (el.jquery) {
2764 el.attr('src', uri);
2765 } else {
2766 el.src = uri;
2767 }
2768 return holla;
2769 },
2770 createStream: function(opt, cb) {
2771 var err, succ;
2772 if (getUserMedia == null) {
2773 return cb("Missing getUserMedia");
2774 }
2775 err = cb;
2776 succ = function(s) {
2777 return cb(null, s);
2778 };
2779 getUserMedia.call(navigator, opt, succ, err);
2780 return holla;
2781 },
2782 createFullStream: function(cb) {
2783 return holla.createStream({
2784 video: true,
2785 audio: true
2786 }, cb);
2787 },
2788 createVideoStream: function(cb) {
2789 return holla.createStream({
2790 video: true,
2791 audio: false
2792 }, cb);
2793 },
2794 createAudioStream: function(cb) {
2795 return holla.createStream({
2796 video: true,
2797 audio: false
2798 }, cb);
2799 }
2800 };
2801
2802 window.holla = holla;
2803
2804}).call(this);