juggernaut 2.0.3 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,139 @@
1
+ // For sprockets:
2
+ //
3
+ //= require json
4
+ //= require socket_io
5
+
6
+ var Juggernaut = function(options){
7
+ this.options = options || {};
8
+
9
+ this.options.host = this.options.host || window.location.hostname;
10
+ this.options.port = this.options.port || 8080;
11
+
12
+ this.handlers = {};
13
+ this.meta = this.options.meta;
14
+
15
+ this.io = io.connect(this.options.host, this.options);
16
+
17
+ this.io.on("connect", this.proxy(this.onconnect));
18
+ this.io.on("message", this.proxy(this.onmessage));
19
+ this.io.on("disconnect", this.proxy(this.ondisconnect));
20
+
21
+ this.on("connect", this.proxy(this.writeMeta));
22
+ };
23
+
24
+ // Helper methods
25
+
26
+ Juggernaut.fn = Juggernaut.prototype;
27
+ Juggernaut.fn.proxy = function(func){
28
+ var thisObject = this;
29
+ return(function(){ return func.apply(thisObject, arguments); });
30
+ };
31
+
32
+ // Public methods
33
+
34
+ Juggernaut.fn.on = function(name, callback){
35
+ if ( !name || !callback ) return;
36
+ if ( !this.handlers[name] ) this.handlers[name] = [];
37
+ this.handlers[name].push(callback);
38
+ };
39
+ Juggernaut.fn.bind = Juggernaut.fn.on;
40
+
41
+ Juggernaut.fn.unbind = function(name){
42
+ if (!this.handlers) return;
43
+ delete this.handlers[name];
44
+ };
45
+
46
+ Juggernaut.fn.write = function(message){
47
+ if (typeof message.toJSON == "function")
48
+ message = message.toJSON();
49
+
50
+ this.io.send(message);
51
+ };
52
+
53
+ Juggernaut.fn.subscribe = function(channel, callback){
54
+ if ( !channel ) throw "Must provide a channel";
55
+
56
+ this.on(channel + ":data", callback);
57
+
58
+ var connectCallback = this.proxy(function(){
59
+ var message = new Juggernaut.Message;
60
+ message.type = "subscribe";
61
+ message.channel = channel;
62
+
63
+ this.write(message);
64
+ });
65
+
66
+ if (this.io.socket.connected)
67
+ connectCallback();
68
+ else {
69
+ this.on("connect", connectCallback);
70
+ }
71
+ };
72
+
73
+ Juggernaut.fn.unsubscribe = function(channel) {
74
+ if ( !channel ) throw "Must provide a channel";
75
+
76
+ this.unbind(channel + ":data");
77
+
78
+ var message = new Juggernaut.Message;
79
+ message.type = "unsubscribe";
80
+ message.channel = channel;
81
+
82
+ this.write(message);
83
+ };
84
+
85
+ // Private
86
+
87
+ Juggernaut.fn.trigger = function(){
88
+ var args = [];
89
+ for (var f=0; f < arguments.length; f++) args.push(arguments[f]);
90
+
91
+ var name = args.shift();
92
+
93
+ var callbacks = this.handlers[name];
94
+ if ( !callbacks ) return;
95
+
96
+ for(var i=0, len = callbacks.length; i < len; i++)
97
+ callbacks[i].apply(this, args);
98
+ };
99
+
100
+ Juggernaut.fn.writeMeta = function(){
101
+ if ( !this.meta ) return;
102
+ var message = new Juggernaut.Message;
103
+ message.type = "meta";
104
+ message.data = this.meta;
105
+ this.write(message);
106
+ };
107
+
108
+ Juggernaut.fn.onconnect = function(){
109
+ this.sessionID = this.io.socket.sessionid;
110
+ this.trigger("connect");
111
+ };
112
+
113
+ Juggernaut.fn.ondisconnect = function(){
114
+ this.trigger("disconnect");
115
+ };
116
+
117
+ Juggernaut.fn.onmessage = function(data){
118
+ var message = Juggernaut.Message.fromJSON(data);
119
+ this.trigger("message", message);
120
+ this.trigger("data", message.channel, message.data);
121
+ this.trigger(message.channel + ":data", message.data);
122
+ };
123
+
124
+ Juggernaut.Message = function(hash){
125
+ for (var key in hash) this[key] = hash[key];
126
+ };
127
+
128
+ Juggernaut.Message.fromJSON = function(json){
129
+ return(new this(JSON.parse(json)))
130
+ };
131
+
132
+ Juggernaut.Message.prototype.toJSON = function(){
133
+ var object = {};
134
+ for (var key in this) {
135
+ if (typeof this[key] != "function")
136
+ object[key] = this[key];
137
+ }
138
+ return(JSON.stringify(object));
139
+ };
@@ -0,0 +1,3707 @@
1
+ /*! Socket.IO.js build:0.8.4, development. Copyright(c) 2011 LearnBoost <dev@learnboost.com> MIT Licensed */
2
+
3
+ /**
4
+ * socket.io
5
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
6
+ * MIT Licensed
7
+ */
8
+
9
+ (function (exports) {
10
+
11
+ /**
12
+ * IO namespace.
13
+ *
14
+ * @namespace
15
+ */
16
+
17
+ var io = exports;
18
+
19
+ /**
20
+ * Socket.IO version
21
+ *
22
+ * @api public
23
+ */
24
+
25
+ io.version = '0.8.4';
26
+
27
+ /**
28
+ * Protocol implemented.
29
+ *
30
+ * @api public
31
+ */
32
+
33
+ io.protocol = 1;
34
+
35
+ /**
36
+ * Available transports, these will be populated with the available transports
37
+ *
38
+ * @api public
39
+ */
40
+
41
+ io.transports = [];
42
+
43
+ /**
44
+ * Keep track of jsonp callbacks.
45
+ *
46
+ * @api private
47
+ */
48
+
49
+ io.j = [];
50
+
51
+ /**
52
+ * Keep track of our io.Sockets
53
+ *
54
+ * @api private
55
+ */
56
+ io.sockets = {};
57
+
58
+
59
+ /**
60
+ * Manages connections to hosts.
61
+ *
62
+ * @param {String} uri
63
+ * @Param {Boolean} force creation of new socket (defaults to false)
64
+ * @api public
65
+ */
66
+
67
+ io.connect = function (host, details) {
68
+ var uri = io.util.parseUri(host)
69
+ , uuri
70
+ , socket;
71
+
72
+ if ('undefined' != typeof document) {
73
+ uri.protocol = uri.protocol || document.location.protocol.slice(0, -1);
74
+ uri.host = uri.host || document.domain;
75
+ uri.port = uri.port || document.location.port;
76
+ }
77
+
78
+ uuri = io.util.uniqueUri(uri);
79
+
80
+ var options = {
81
+ host: uri.host
82
+ , secure: 'https' == uri.protocol
83
+ , port: uri.port || ('https' == uri.protocol ? 443 : 80)
84
+ , query: uri.query || ''
85
+ };
86
+
87
+ io.util.merge(options, details);
88
+
89
+ if (options['force new connection'] || !io.sockets[uuri]) {
90
+ socket = new io.Socket(options);
91
+ }
92
+
93
+ if (!options['force new connection'] && socket) {
94
+ io.sockets[uuri] = socket;
95
+ }
96
+
97
+ socket = socket || io.sockets[uuri];
98
+
99
+ // if path is different from '' or /
100
+ return socket.of(uri.path.length > 1 ? uri.path : '');
101
+ };
102
+
103
+ })('object' === typeof module ? module.exports : (window.io = {}));
104
+
105
+ /**
106
+ * socket.io
107
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
108
+ * MIT Licensed
109
+ */
110
+
111
+ (function (exports, global) {
112
+
113
+ /**
114
+ * Utilities namespace.
115
+ *
116
+ * @namespace
117
+ */
118
+
119
+ var util = exports.util = {};
120
+
121
+ /**
122
+ * Parses an URI
123
+ *
124
+ * @author Steven Levithan <stevenlevithan.com> (MIT license)
125
+ * @api public
126
+ */
127
+
128
+ var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
129
+
130
+ var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password',
131
+ 'host', 'port', 'relative', 'path', 'directory', 'file', 'query',
132
+ 'anchor'];
133
+
134
+ util.parseUri = function (str) {
135
+ var m = re.exec(str || '')
136
+ , uri = {}
137
+ , i = 14;
138
+
139
+ while (i--) {
140
+ uri[parts[i]] = m[i] || '';
141
+ }
142
+
143
+ return uri;
144
+ };
145
+
146
+ /**
147
+ * Produces a unique url that identifies a Socket.IO connection.
148
+ *
149
+ * @param {Object} uri
150
+ * @api public
151
+ */
152
+
153
+ util.uniqueUri = function (uri) {
154
+ var protocol = uri.protocol
155
+ , host = uri.host
156
+ , port = uri.port;
157
+
158
+ if ('document' in global) {
159
+ host = host || document.domain;
160
+ port = port || (protocol == 'https'
161
+ && document.location.protocol !== 'https:' ? 443 : document.location.port);
162
+ } else {
163
+ host = host || 'localhost';
164
+
165
+ if (!port && protocol == 'https') {
166
+ port = 443;
167
+ }
168
+ }
169
+
170
+ return (protocol || 'http') + '://' + host + ':' + (port || 80);
171
+ };
172
+
173
+ /**
174
+ * Mergest 2 query strings in to once unique query string
175
+ *
176
+ * @param {String} base
177
+ * @param {String} addition
178
+ * @api public
179
+ */
180
+
181
+ util.query = function (base, addition) {
182
+ var query = util.chunkQuery(base || '')
183
+ , components = [];
184
+
185
+ util.merge(query, util.chunkQuery(addition || ''));
186
+ for (var part in query) {
187
+ if (query.hasOwnProperty(part)) {
188
+ components.push(part + '=' + query[part]);
189
+ }
190
+ }
191
+
192
+ return components.length ? '?' + components.join('&') : '';
193
+ };
194
+
195
+ /**
196
+ * Transforms a querystring in to an object
197
+ *
198
+ * @param {String} qs
199
+ * @api public
200
+ */
201
+
202
+ util.chunkQuery = function (qs) {
203
+ var query = {}
204
+ , params = qs.split('&')
205
+ , i = 0
206
+ , l = params.length
207
+ , kv;
208
+
209
+ for (; i < l; ++i) {
210
+ kv = params[i].split('=');
211
+ if (kv[0]) {
212
+ query[kv[0]] = decodeURIComponent(kv[1]);
213
+ }
214
+ }
215
+
216
+ return query;
217
+ };
218
+
219
+ /**
220
+ * Executes the given function when the page is loaded.
221
+ *
222
+ * io.util.load(function () { console.log('page loaded'); });
223
+ *
224
+ * @param {Function} fn
225
+ * @api public
226
+ */
227
+
228
+ var pageLoaded = false;
229
+
230
+ util.load = function (fn) {
231
+ if ('document' in global && document.readyState === 'complete' || pageLoaded) {
232
+ return fn();
233
+ }
234
+
235
+ util.on(global, 'load', fn, false);
236
+ };
237
+
238
+ /**
239
+ * Adds an event.
240
+ *
241
+ * @api private
242
+ */
243
+
244
+ util.on = function (element, event, fn, capture) {
245
+ if (element.attachEvent) {
246
+ element.attachEvent('on' + event, fn);
247
+ } else if (element.addEventListener) {
248
+ element.addEventListener(event, fn, capture);
249
+ }
250
+ };
251
+
252
+ /**
253
+ * Generates the correct `XMLHttpRequest` for regular and cross domain requests.
254
+ *
255
+ * @param {Boolean} [xdomain] Create a request that can be used cross domain.
256
+ * @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest.
257
+ * @api private
258
+ */
259
+
260
+ util.request = function (xdomain) {
261
+
262
+ if ('undefined' != typeof window) {
263
+ if (xdomain && window.XDomainRequest) {
264
+ return new XDomainRequest();
265
+ }
266
+
267
+ if (window.XMLHttpRequest && (!xdomain || util.ua.hasCORS)) {
268
+ return new XMLHttpRequest();
269
+ }
270
+
271
+ if (!xdomain) {
272
+ try {
273
+ return new window.ActiveXObject('Microsoft.XMLHTTP');
274
+ } catch(e) { }
275
+ }
276
+ }
277
+
278
+ return null;
279
+ };
280
+
281
+ /**
282
+ * XHR based transport constructor.
283
+ *
284
+ * @constructor
285
+ * @api public
286
+ */
287
+
288
+ /**
289
+ * Change the internal pageLoaded value.
290
+ */
291
+
292
+ if ('undefined' != typeof window) {
293
+ util.load(function () {
294
+ pageLoaded = true;
295
+ });
296
+ }
297
+
298
+ /**
299
+ * Defers a function to ensure a spinner is not displayed by the browser
300
+ *
301
+ * @param {Function} fn
302
+ * @api public
303
+ */
304
+
305
+ util.defer = function (fn) {
306
+ if (!util.ua.webkit) {
307
+ return fn();
308
+ }
309
+
310
+ util.load(function () {
311
+ setTimeout(fn, 100);
312
+ });
313
+ };
314
+
315
+ /**
316
+ * Merges two objects.
317
+ *
318
+ * @api public
319
+ */
320
+
321
+ util.merge = function merge (target, additional, deep, lastseen) {
322
+ var seen = lastseen || []
323
+ , depth = typeof deep == 'undefined' ? 2 : deep
324
+ , prop;
325
+
326
+ for (prop in additional) {
327
+ if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) {
328
+ if (typeof target[prop] !== 'object' || !depth) {
329
+ target[prop] = additional[prop];
330
+ seen.push(additional[prop]);
331
+ } else {
332
+ util.merge(target[prop], additional[prop], depth - 1, seen);
333
+ }
334
+ }
335
+ }
336
+
337
+ return target;
338
+ };
339
+
340
+ /**
341
+ * Merges prototypes from objects
342
+ *
343
+ * @api public
344
+ */
345
+
346
+ util.mixin = function (ctor, ctor2) {
347
+ util.merge(ctor.prototype, ctor2.prototype);
348
+ };
349
+
350
+ /**
351
+ * Shortcut for prototypical and static inheritance.
352
+ *
353
+ * @api private
354
+ */
355
+
356
+ util.inherit = function (ctor, ctor2) {
357
+ function f() {};
358
+ f.prototype = ctor2.prototype;
359
+ ctor.prototype = new f;
360
+ };
361
+
362
+ /**
363
+ * Checks if the given object is an Array.
364
+ *
365
+ * io.util.isArray([]); // true
366
+ * io.util.isArray({}); // false
367
+ *
368
+ * @param Object obj
369
+ * @api public
370
+ */
371
+
372
+ util.isArray = Array.isArray || function (obj) {
373
+ return Object.prototype.toString.call(obj) === '[object Array]';
374
+ };
375
+
376
+ /**
377
+ * Intersects values of two arrays into a third
378
+ *
379
+ * @api public
380
+ */
381
+
382
+ util.intersect = function (arr, arr2) {
383
+ var ret = []
384
+ , longest = arr.length > arr2.length ? arr : arr2
385
+ , shortest = arr.length > arr2.length ? arr2 : arr;
386
+
387
+ for (var i = 0, l = shortest.length; i < l; i++) {
388
+ if (~util.indexOf(longest, shortest[i]))
389
+ ret.push(shortest[i]);
390
+ }
391
+
392
+ return ret;
393
+ }
394
+
395
+ /**
396
+ * Array indexOf compatibility.
397
+ *
398
+ * @see bit.ly/a5Dxa2
399
+ * @api public
400
+ */
401
+
402
+ util.indexOf = function (arr, o, i) {
403
+ if (Array.prototype.indexOf) {
404
+ return Array.prototype.indexOf.call(arr, o, i);
405
+ }
406
+
407
+ for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0;
408
+ i < j && arr[i] !== o; i++);
409
+
410
+ return j <= i ? -1 : i;
411
+ };
412
+
413
+ /**
414
+ * Converts enumerables to array.
415
+ *
416
+ * @api public
417
+ */
418
+
419
+ util.toArray = function (enu) {
420
+ var arr = [];
421
+
422
+ for (var i = 0, l = enu.length; i < l; i++)
423
+ arr.push(enu[i]);
424
+
425
+ return arr;
426
+ };
427
+
428
+ /**
429
+ * UA / engines detection namespace.
430
+ *
431
+ * @namespace
432
+ */
433
+
434
+ util.ua = {};
435
+
436
+ /**
437
+ * Whether the UA supports CORS for XHR.
438
+ *
439
+ * @api public
440
+ */
441
+
442
+ util.ua.hasCORS = 'undefined' != typeof window && window.XMLHttpRequest &&
443
+ (function () {
444
+ try {
445
+ var a = new XMLHttpRequest();
446
+ } catch (e) {
447
+ return false;
448
+ }
449
+
450
+ return a.withCredentials != undefined;
451
+ })();
452
+
453
+ /**
454
+ * Detect webkit.
455
+ *
456
+ * @api public
457
+ */
458
+
459
+ util.ua.webkit = 'undefined' != typeof navigator
460
+ && /webkit/i.test(navigator.userAgent);
461
+
462
+ })(
463
+ 'undefined' != typeof window ? io : module.exports
464
+ , this
465
+ );
466
+
467
+ /**
468
+ * socket.io
469
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
470
+ * MIT Licensed
471
+ */
472
+
473
+ (function (exports, io) {
474
+
475
+ /**
476
+ * Expose constructor.
477
+ */
478
+
479
+ exports.EventEmitter = EventEmitter;
480
+
481
+ /**
482
+ * Event emitter constructor.
483
+ *
484
+ * @api public.
485
+ */
486
+
487
+ function EventEmitter () {};
488
+
489
+ /**
490
+ * Adds a listener
491
+ *
492
+ * @api public
493
+ */
494
+
495
+ EventEmitter.prototype.on = function (name, fn) {
496
+ if (!this.$events) {
497
+ this.$events = {};
498
+ }
499
+
500
+ if (!this.$events[name]) {
501
+ this.$events[name] = fn;
502
+ } else if (io.util.isArray(this.$events[name])) {
503
+ this.$events[name].push(fn);
504
+ } else {
505
+ this.$events[name] = [this.$events[name], fn];
506
+ }
507
+
508
+ return this;
509
+ };
510
+
511
+ EventEmitter.prototype.addListener = EventEmitter.prototype.on;
512
+
513
+ /**
514
+ * Adds a volatile listener.
515
+ *
516
+ * @api public
517
+ */
518
+
519
+ EventEmitter.prototype.once = function (name, fn) {
520
+ var self = this;
521
+
522
+ function on () {
523
+ self.removeListener(name, on);
524
+ fn.apply(this, arguments);
525
+ };
526
+
527
+ on.listener = fn;
528
+ this.on(name, on);
529
+
530
+ return this;
531
+ };
532
+
533
+ /**
534
+ * Removes a listener.
535
+ *
536
+ * @api public
537
+ */
538
+
539
+ EventEmitter.prototype.removeListener = function (name, fn) {
540
+ if (this.$events && this.$events[name]) {
541
+ var list = this.$events[name];
542
+
543
+ if (io.util.isArray(list)) {
544
+ var pos = -1;
545
+
546
+ for (var i = 0, l = list.length; i < l; i++) {
547
+ if (list[i] === fn || (list[i].listener && list[i].listener === fn)) {
548
+ pos = i;
549
+ break;
550
+ }
551
+ }
552
+
553
+ if (pos < 0) {
554
+ return this;
555
+ }
556
+
557
+ list.splice(pos, 1);
558
+
559
+ if (!list.length) {
560
+ delete this.$events[name];
561
+ }
562
+ } else if (list === fn || (list.listener && list.listener === fn)) {
563
+ delete this.$events[name];
564
+ }
565
+ }
566
+
567
+ return this;
568
+ };
569
+
570
+ /**
571
+ * Removes all listeners for an event.
572
+ *
573
+ * @api public
574
+ */
575
+
576
+ EventEmitter.prototype.removeAllListeners = function (name) {
577
+ // TODO: enable this when node 0.5 is stable
578
+ //if (name === undefined) {
579
+ //this.$events = {};
580
+ //return this;
581
+ //}
582
+
583
+ if (this.$events && this.$events[name]) {
584
+ this.$events[name] = null;
585
+ }
586
+
587
+ return this;
588
+ };
589
+
590
+ /**
591
+ * Gets all listeners for a certain event.
592
+ *
593
+ * @api publci
594
+ */
595
+
596
+ EventEmitter.prototype.listeners = function (name) {
597
+ if (!this.$events) {
598
+ this.$events = {};
599
+ }
600
+
601
+ if (!this.$events[name]) {
602
+ this.$events[name] = [];
603
+ }
604
+
605
+ if (!io.util.isArray(this.$events[name])) {
606
+ this.$events[name] = [this.$events[name]];
607
+ }
608
+
609
+ return this.$events[name];
610
+ };
611
+
612
+ /**
613
+ * Emits an event.
614
+ *
615
+ * @api public
616
+ */
617
+
618
+ EventEmitter.prototype.emit = function (name) {
619
+ if (!this.$events) {
620
+ return false;
621
+ }
622
+
623
+ var handler = this.$events[name];
624
+
625
+ if (!handler) {
626
+ return false;
627
+ }
628
+
629
+ var args = Array.prototype.slice.call(arguments, 1);
630
+
631
+ if ('function' == typeof handler) {
632
+ handler.apply(this, args);
633
+ } else if (io.util.isArray(handler)) {
634
+ var listeners = handler.slice();
635
+
636
+ for (var i = 0, l = listeners.length; i < l; i++) {
637
+ listeners[i].apply(this, args);
638
+ }
639
+ } else {
640
+ return false;
641
+ }
642
+
643
+ return true;
644
+ };
645
+
646
+ })(
647
+ 'undefined' != typeof io ? io : module.exports
648
+ , 'undefined' != typeof io ? io : module.parent.exports
649
+ );
650
+
651
+ /**
652
+ * socket.io
653
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
654
+ * MIT Licensed
655
+ */
656
+
657
+ /**
658
+ * Based on JSON2 (http://www.JSON.org/js.html).
659
+ */
660
+
661
+ (function (exports, nativeJSON) {
662
+ "use strict";
663
+
664
+ // use native JSON if it's available
665
+ if (nativeJSON && nativeJSON.parse){
666
+ return exports.JSON = {
667
+ parse: nativeJSON.parse
668
+ , stringify: nativeJSON.stringify
669
+ }
670
+ }
671
+
672
+ var JSON = exports.JSON = {};
673
+
674
+ function f(n) {
675
+ // Format integers to have at least two digits.
676
+ return n < 10 ? '0' + n : n;
677
+ }
678
+
679
+ function date(d, key) {
680
+ return isFinite(d.valueOf()) ?
681
+ d.getUTCFullYear() + '-' +
682
+ f(d.getUTCMonth() + 1) + '-' +
683
+ f(d.getUTCDate()) + 'T' +
684
+ f(d.getUTCHours()) + ':' +
685
+ f(d.getUTCMinutes()) + ':' +
686
+ f(d.getUTCSeconds()) + 'Z' : null;
687
+ };
688
+
689
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
690
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
691
+ gap,
692
+ indent,
693
+ meta = { // table of character substitutions
694
+ '\b': '\\b',
695
+ '\t': '\\t',
696
+ '\n': '\\n',
697
+ '\f': '\\f',
698
+ '\r': '\\r',
699
+ '"' : '\\"',
700
+ '\\': '\\\\'
701
+ },
702
+ rep;
703
+
704
+
705
+ function quote(string) {
706
+
707
+ // If the string contains no control characters, no quote characters, and no
708
+ // backslash characters, then we can safely slap some quotes around it.
709
+ // Otherwise we must also replace the offending characters with safe escape
710
+ // sequences.
711
+
712
+ escapable.lastIndex = 0;
713
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
714
+ var c = meta[a];
715
+ return typeof c === 'string' ? c :
716
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
717
+ }) + '"' : '"' + string + '"';
718
+ }
719
+
720
+
721
+ function str(key, holder) {
722
+
723
+ // Produce a string from holder[key].
724
+
725
+ var i, // The loop counter.
726
+ k, // The member key.
727
+ v, // The member value.
728
+ length,
729
+ mind = gap,
730
+ partial,
731
+ value = holder[key];
732
+
733
+ // If the value has a toJSON method, call it to obtain a replacement value.
734
+
735
+ if (value instanceof Date) {
736
+ value = date(key);
737
+ }
738
+
739
+ // If we were called with a replacer function, then call the replacer to
740
+ // obtain a replacement value.
741
+
742
+ if (typeof rep === 'function') {
743
+ value = rep.call(holder, key, value);
744
+ }
745
+
746
+ // What happens next depends on the value's type.
747
+
748
+ switch (typeof value) {
749
+ case 'string':
750
+ return quote(value);
751
+
752
+ case 'number':
753
+
754
+ // JSON numbers must be finite. Encode non-finite numbers as null.
755
+
756
+ return isFinite(value) ? String(value) : 'null';
757
+
758
+ case 'boolean':
759
+ case 'null':
760
+
761
+ // If the value is a boolean or null, convert it to a string. Note:
762
+ // typeof null does not produce 'null'. The case is included here in
763
+ // the remote chance that this gets fixed someday.
764
+
765
+ return String(value);
766
+
767
+ // If the type is 'object', we might be dealing with an object or an array or
768
+ // null.
769
+
770
+ case 'object':
771
+
772
+ // Due to a specification blunder in ECMAScript, typeof null is 'object',
773
+ // so watch out for that case.
774
+
775
+ if (!value) {
776
+ return 'null';
777
+ }
778
+
779
+ // Make an array to hold the partial results of stringifying this object value.
780
+
781
+ gap += indent;
782
+ partial = [];
783
+
784
+ // Is the value an array?
785
+
786
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
787
+
788
+ // The value is an array. Stringify every element. Use null as a placeholder
789
+ // for non-JSON values.
790
+
791
+ length = value.length;
792
+ for (i = 0; i < length; i += 1) {
793
+ partial[i] = str(i, value) || 'null';
794
+ }
795
+
796
+ // Join all of the elements together, separated with commas, and wrap them in
797
+ // brackets.
798
+
799
+ v = partial.length === 0 ? '[]' : gap ?
800
+ '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
801
+ '[' + partial.join(',') + ']';
802
+ gap = mind;
803
+ return v;
804
+ }
805
+
806
+ // If the replacer is an array, use it to select the members to be stringified.
807
+
808
+ if (rep && typeof rep === 'object') {
809
+ length = rep.length;
810
+ for (i = 0; i < length; i += 1) {
811
+ if (typeof rep[i] === 'string') {
812
+ k = rep[i];
813
+ v = str(k, value);
814
+ if (v) {
815
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
816
+ }
817
+ }
818
+ }
819
+ } else {
820
+
821
+ // Otherwise, iterate through all of the keys in the object.
822
+
823
+ for (k in value) {
824
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
825
+ v = str(k, value);
826
+ if (v) {
827
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
828
+ }
829
+ }
830
+ }
831
+ }
832
+
833
+ // Join all of the member texts together, separated with commas,
834
+ // and wrap them in braces.
835
+
836
+ v = partial.length === 0 ? '{}' : gap ?
837
+ '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
838
+ '{' + partial.join(',') + '}';
839
+ gap = mind;
840
+ return v;
841
+ }
842
+ }
843
+
844
+ // If the JSON object does not yet have a stringify method, give it one.
845
+
846
+ JSON.stringify = function (value, replacer, space) {
847
+
848
+ // The stringify method takes a value and an optional replacer, and an optional
849
+ // space parameter, and returns a JSON text. The replacer can be a function
850
+ // that can replace values, or an array of strings that will select the keys.
851
+ // A default replacer method can be provided. Use of the space parameter can
852
+ // produce text that is more easily readable.
853
+
854
+ var i;
855
+ gap = '';
856
+ indent = '';
857
+
858
+ // If the space parameter is a number, make an indent string containing that
859
+ // many spaces.
860
+
861
+ if (typeof space === 'number') {
862
+ for (i = 0; i < space; i += 1) {
863
+ indent += ' ';
864
+ }
865
+
866
+ // If the space parameter is a string, it will be used as the indent string.
867
+
868
+ } else if (typeof space === 'string') {
869
+ indent = space;
870
+ }
871
+
872
+ // If there is a replacer, it must be a function or an array.
873
+ // Otherwise, throw an error.
874
+
875
+ rep = replacer;
876
+ if (replacer && typeof replacer !== 'function' &&
877
+ (typeof replacer !== 'object' ||
878
+ typeof replacer.length !== 'number')) {
879
+ throw new Error('JSON.stringify');
880
+ }
881
+
882
+ // Make a fake root object containing our value under the key of ''.
883
+ // Return the result of stringifying the value.
884
+
885
+ return str('', {'': value});
886
+ };
887
+
888
+ // If the JSON object does not yet have a parse method, give it one.
889
+
890
+ JSON.parse = function (text, reviver) {
891
+ // The parse method takes a text and an optional reviver function, and returns
892
+ // a JavaScript value if the text is a valid JSON text.
893
+
894
+ var j;
895
+
896
+ function walk(holder, key) {
897
+
898
+ // The walk method is used to recursively walk the resulting structure so
899
+ // that modifications can be made.
900
+
901
+ var k, v, value = holder[key];
902
+ if (value && typeof value === 'object') {
903
+ for (k in value) {
904
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
905
+ v = walk(value, k);
906
+ if (v !== undefined) {
907
+ value[k] = v;
908
+ } else {
909
+ delete value[k];
910
+ }
911
+ }
912
+ }
913
+ }
914
+ return reviver.call(holder, key, value);
915
+ }
916
+
917
+
918
+ // Parsing happens in four stages. In the first stage, we replace certain
919
+ // Unicode characters with escape sequences. JavaScript handles many characters
920
+ // incorrectly, either silently deleting them, or treating them as line endings.
921
+
922
+ text = String(text);
923
+ cx.lastIndex = 0;
924
+ if (cx.test(text)) {
925
+ text = text.replace(cx, function (a) {
926
+ return '\\u' +
927
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
928
+ });
929
+ }
930
+
931
+ // In the second stage, we run the text against regular expressions that look
932
+ // for non-JSON patterns. We are especially concerned with '()' and 'new'
933
+ // because they can cause invocation, and '=' because it can cause mutation.
934
+ // But just to be safe, we want to reject all unexpected forms.
935
+
936
+ // We split the second stage into 4 regexp operations in order to work around
937
+ // crippling inefficiencies in IE's and Safari's regexp engines. First we
938
+ // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
939
+ // replace all simple value tokens with ']' characters. Third, we delete all
940
+ // open brackets that follow a colon or comma or that begin the text. Finally,
941
+ // we look to see that the remaining characters are only whitespace or ']' or
942
+ // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
943
+
944
+ if (/^[\],:{}\s]*$/
945
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
946
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
947
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
948
+
949
+ // In the third stage we use the eval function to compile the text into a
950
+ // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
951
+ // in JavaScript: it can begin a block or an object literal. We wrap the text
952
+ // in parens to eliminate the ambiguity.
953
+
954
+ j = eval('(' + text + ')');
955
+
956
+ // In the optional fourth stage, we recursively walk the new structure, passing
957
+ // each name/value pair to a reviver function for possible transformation.
958
+
959
+ return typeof reviver === 'function' ?
960
+ walk({'': j}, '') : j;
961
+ }
962
+
963
+ // If the text is not JSON parseable, then a SyntaxError is thrown.
964
+
965
+ throw new SyntaxError('JSON.parse');
966
+ };
967
+
968
+ })(
969
+ 'undefined' != typeof io ? io : module.exports
970
+ , typeof JSON !== 'undefined' ? JSON : undefined
971
+ );
972
+
973
+ /**
974
+ * socket.io
975
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
976
+ * MIT Licensed
977
+ */
978
+
979
+ (function (exports, io) {
980
+
981
+ /**
982
+ * Parser namespace.
983
+ *
984
+ * @namespace
985
+ */
986
+
987
+ var parser = exports.parser = {};
988
+
989
+ /**
990
+ * Packet types.
991
+ */
992
+
993
+ var packets = parser.packets = [
994
+ 'disconnect'
995
+ , 'connect'
996
+ , 'heartbeat'
997
+ , 'message'
998
+ , 'json'
999
+ , 'event'
1000
+ , 'ack'
1001
+ , 'error'
1002
+ , 'noop'
1003
+ ];
1004
+
1005
+ /**
1006
+ * Errors reasons.
1007
+ */
1008
+
1009
+ var reasons = parser.reasons = [
1010
+ 'transport not supported'
1011
+ , 'client not handshaken'
1012
+ , 'unauthorized'
1013
+ ];
1014
+
1015
+ /**
1016
+ * Errors advice.
1017
+ */
1018
+
1019
+ var advice = parser.advice = [
1020
+ 'reconnect'
1021
+ ];
1022
+
1023
+ /**
1024
+ * Shortcuts.
1025
+ */
1026
+
1027
+ var JSON = io.JSON
1028
+ , indexOf = io.util.indexOf;
1029
+
1030
+ /**
1031
+ * Encodes a packet.
1032
+ *
1033
+ * @api private
1034
+ */
1035
+
1036
+ parser.encodePacket = function (packet) {
1037
+ var type = indexOf(packets, packet.type)
1038
+ , id = packet.id || ''
1039
+ , endpoint = packet.endpoint || ''
1040
+ , ack = packet.ack
1041
+ , data = null;
1042
+
1043
+ switch (packet.type) {
1044
+ case 'error':
1045
+ var reason = packet.reason ? indexOf(reasons, packet.reason) : ''
1046
+ , adv = packet.advice ? indexOf(advice, packet.advice) : '';
1047
+
1048
+ if (reason !== '' || adv !== '')
1049
+ data = reason + (adv !== '' ? ('+' + adv) : '');
1050
+
1051
+ break;
1052
+
1053
+ case 'message':
1054
+ if (packet.data !== '')
1055
+ data = packet.data;
1056
+ break;
1057
+
1058
+ case 'event':
1059
+ var ev = { name: packet.name };
1060
+
1061
+ if (packet.args && packet.args.length) {
1062
+ ev.args = packet.args;
1063
+ }
1064
+
1065
+ data = JSON.stringify(ev);
1066
+ break;
1067
+
1068
+ case 'json':
1069
+ data = JSON.stringify(packet.data);
1070
+ break;
1071
+
1072
+ case 'connect':
1073
+ if (packet.qs)
1074
+ data = packet.qs;
1075
+ break;
1076
+
1077
+ case 'ack':
1078
+ data = packet.ackId
1079
+ + (packet.args && packet.args.length
1080
+ ? '+' + JSON.stringify(packet.args) : '');
1081
+ break;
1082
+ }
1083
+
1084
+ // construct packet with required fragments
1085
+ var encoded = [
1086
+ type
1087
+ , id + (ack == 'data' ? '+' : '')
1088
+ , endpoint
1089
+ ];
1090
+
1091
+ // data fragment is optional
1092
+ if (data !== null && data !== undefined)
1093
+ encoded.push(data);
1094
+
1095
+ return encoded.join(':');
1096
+ };
1097
+
1098
+ /**
1099
+ * Encodes multiple messages (payload).
1100
+ *
1101
+ * @param {Array} messages
1102
+ * @api private
1103
+ */
1104
+
1105
+ parser.encodePayload = function (packets) {
1106
+ var decoded = '';
1107
+
1108
+ if (packets.length == 1)
1109
+ return packets[0];
1110
+
1111
+ for (var i = 0, l = packets.length; i < l; i++) {
1112
+ var packet = packets[i];
1113
+ decoded += '\ufffd' + packet.length + '\ufffd' + packets[i];
1114
+ }
1115
+
1116
+ return decoded;
1117
+ };
1118
+
1119
+ /**
1120
+ * Decodes a packet
1121
+ *
1122
+ * @api private
1123
+ */
1124
+
1125
+ var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/;
1126
+
1127
+ parser.decodePacket = function (data) {
1128
+ var pieces = data.match(regexp);
1129
+
1130
+ if (!pieces) return {};
1131
+
1132
+ var id = pieces[2] || ''
1133
+ , data = pieces[5] || ''
1134
+ , packet = {
1135
+ type: packets[pieces[1]]
1136
+ , endpoint: pieces[4] || ''
1137
+ };
1138
+
1139
+ // whether we need to acknowledge the packet
1140
+ if (id) {
1141
+ packet.id = id;
1142
+ if (pieces[3])
1143
+ packet.ack = 'data';
1144
+ else
1145
+ packet.ack = true;
1146
+ }
1147
+
1148
+ // handle different packet types
1149
+ switch (packet.type) {
1150
+ case 'error':
1151
+ var pieces = data.split('+');
1152
+ packet.reason = reasons[pieces[0]] || '';
1153
+ packet.advice = advice[pieces[1]] || '';
1154
+ break;
1155
+
1156
+ case 'message':
1157
+ packet.data = data || '';
1158
+ break;
1159
+
1160
+ case 'event':
1161
+ try {
1162
+ var opts = JSON.parse(data);
1163
+ packet.name = opts.name;
1164
+ packet.args = opts.args;
1165
+ } catch (e) { }
1166
+
1167
+ packet.args = packet.args || [];
1168
+ break;
1169
+
1170
+ case 'json':
1171
+ try {
1172
+ packet.data = JSON.parse(data);
1173
+ } catch (e) { }
1174
+ break;
1175
+
1176
+ case 'connect':
1177
+ packet.qs = data || '';
1178
+ break;
1179
+
1180
+ case 'ack':
1181
+ var pieces = data.match(/^([0-9]+)(\+)?(.*)/);
1182
+ if (pieces) {
1183
+ packet.ackId = pieces[1];
1184
+ packet.args = [];
1185
+
1186
+ if (pieces[3]) {
1187
+ try {
1188
+ packet.args = pieces[3] ? JSON.parse(pieces[3]) : [];
1189
+ } catch (e) { }
1190
+ }
1191
+ }
1192
+ break;
1193
+
1194
+ case 'disconnect':
1195
+ case 'heartbeat':
1196
+ break;
1197
+ };
1198
+
1199
+ return packet;
1200
+ };
1201
+
1202
+ /**
1203
+ * Decodes data payload. Detects multiple messages
1204
+ *
1205
+ * @return {Array} messages
1206
+ * @api public
1207
+ */
1208
+
1209
+ parser.decodePayload = function (data) {
1210
+ // IE doesn't like data[i] for unicode chars, charAt works fine
1211
+ if (data.charAt(0) == '\ufffd') {
1212
+ var ret = [];
1213
+
1214
+ for (var i = 1, length = ''; i < data.length; i++) {
1215
+ if (data.charAt(i) == '\ufffd') {
1216
+ ret.push(parser.decodePacket(data.substr(i + 1).substr(0, length)));
1217
+ i += Number(length) + 1;
1218
+ length = '';
1219
+ } else {
1220
+ length += data.charAt(i);
1221
+ }
1222
+ }
1223
+
1224
+ return ret;
1225
+ } else {
1226
+ return [parser.decodePacket(data)];
1227
+ }
1228
+ };
1229
+
1230
+ })(
1231
+ 'undefined' != typeof io ? io : module.exports
1232
+ , 'undefined' != typeof io ? io : module.parent.exports
1233
+ );
1234
+
1235
+ /**
1236
+ * socket.io
1237
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
1238
+ * MIT Licensed
1239
+ */
1240
+
1241
+ (function (exports, io) {
1242
+
1243
+ /**
1244
+ * Expose constructor.
1245
+ */
1246
+
1247
+ exports.Transport = Transport;
1248
+
1249
+ /**
1250
+ * This is the transport template for all supported transport methods.
1251
+ *
1252
+ * @constructor
1253
+ * @api public
1254
+ */
1255
+
1256
+ function Transport (socket, sessid) {
1257
+ this.socket = socket;
1258
+ this.sessid = sessid;
1259
+ };
1260
+
1261
+ /**
1262
+ * Apply EventEmitter mixin.
1263
+ */
1264
+
1265
+ io.util.mixin(Transport, io.EventEmitter);
1266
+
1267
+ /**
1268
+ * Handles the response from the server. When a new response is received
1269
+ * it will automatically update the timeout, decode the message and
1270
+ * forwards the response to the onMessage function for further processing.
1271
+ *
1272
+ * @param {String} data Response from the server.
1273
+ * @api private
1274
+ */
1275
+
1276
+ Transport.prototype.onData = function (data) {
1277
+ this.clearCloseTimeout();
1278
+ this.setCloseTimeout();
1279
+
1280
+ if (data !== '') {
1281
+ // todo: we should only do decodePayload for xhr transports
1282
+ var msgs = io.parser.decodePayload(data);
1283
+
1284
+ if (msgs && msgs.length) {
1285
+ for (var i = 0, l = msgs.length; i < l; i++) {
1286
+ this.onPacket(msgs[i]);
1287
+ }
1288
+ }
1289
+ }
1290
+
1291
+ return this;
1292
+ };
1293
+
1294
+ /**
1295
+ * Handles packets.
1296
+ *
1297
+ * @api private
1298
+ */
1299
+
1300
+ Transport.prototype.onPacket = function (packet) {
1301
+ if (packet.type == 'heartbeat') {
1302
+ return this.onHeartbeat();
1303
+ }
1304
+
1305
+ if (packet.type == 'connect' && packet.endpoint == '') {
1306
+ this.onConnect();
1307
+ }
1308
+
1309
+ this.socket.onPacket(packet);
1310
+
1311
+ return this;
1312
+ };
1313
+
1314
+ /**
1315
+ * Sets close timeout
1316
+ *
1317
+ * @api private
1318
+ */
1319
+
1320
+ Transport.prototype.setCloseTimeout = function () {
1321
+ if (!this.closeTimeout) {
1322
+ var self = this;
1323
+
1324
+ this.closeTimeout = setTimeout(function () {
1325
+ self.onDisconnect();
1326
+ }, this.socket.closeTimeout);
1327
+ }
1328
+ };
1329
+
1330
+ /**
1331
+ * Called when transport disconnects.
1332
+ *
1333
+ * @api private
1334
+ */
1335
+
1336
+ Transport.prototype.onDisconnect = function () {
1337
+ if (this.close) this.close();
1338
+ this.clearTimeouts();
1339
+ this.socket.onDisconnect();
1340
+ return this;
1341
+ };
1342
+
1343
+ /**
1344
+ * Called when transport connects
1345
+ *
1346
+ * @api private
1347
+ */
1348
+
1349
+ Transport.prototype.onConnect = function () {
1350
+ this.socket.onConnect();
1351
+ return this;
1352
+ }
1353
+
1354
+ /**
1355
+ * Clears close timeout
1356
+ *
1357
+ * @api private
1358
+ */
1359
+
1360
+ Transport.prototype.clearCloseTimeout = function () {
1361
+ if (this.closeTimeout) {
1362
+ clearTimeout(this.closeTimeout);
1363
+ this.closeTimeout = null;
1364
+ }
1365
+ };
1366
+
1367
+ /**
1368
+ * Clear timeouts
1369
+ *
1370
+ * @api private
1371
+ */
1372
+
1373
+ Transport.prototype.clearTimeouts = function () {
1374
+ this.clearCloseTimeout();
1375
+
1376
+ if (this.reopenTimeout) {
1377
+ clearTimeout(this.reopenTimeout);
1378
+ }
1379
+ };
1380
+
1381
+ /**
1382
+ * Sends a packet
1383
+ *
1384
+ * @param {Object} packet object.
1385
+ * @api private
1386
+ */
1387
+
1388
+ Transport.prototype.packet = function (packet) {
1389
+ this.send(io.parser.encodePacket(packet));
1390
+ };
1391
+
1392
+ /**
1393
+ * Send the received heartbeat message back to server. So the server
1394
+ * knows we are still connected.
1395
+ *
1396
+ * @param {String} heartbeat Heartbeat response from the server.
1397
+ * @api private
1398
+ */
1399
+
1400
+ Transport.prototype.onHeartbeat = function (heartbeat) {
1401
+ this.packet({ type: 'heartbeat' });
1402
+ };
1403
+
1404
+ /**
1405
+ * Called when the transport opens.
1406
+ *
1407
+ * @api private
1408
+ */
1409
+
1410
+ Transport.prototype.onOpen = function () {
1411
+ this.open = true;
1412
+ this.clearCloseTimeout();
1413
+ this.socket.onOpen();
1414
+ };
1415
+
1416
+ /**
1417
+ * Notifies the base when the connection with the Socket.IO server
1418
+ * has been disconnected.
1419
+ *
1420
+ * @api private
1421
+ */
1422
+
1423
+ Transport.prototype.onClose = function () {
1424
+ var self = this;
1425
+
1426
+ /* FIXME: reopen delay causing a infinit loop
1427
+ this.reopenTimeout = setTimeout(function () {
1428
+ self.open();
1429
+ }, this.socket.options['reopen delay']);*/
1430
+
1431
+ this.open = false;
1432
+ this.setCloseTimeout();
1433
+ this.socket.onClose();
1434
+ };
1435
+
1436
+ /**
1437
+ * Generates a connection url based on the Socket.IO URL Protocol.
1438
+ * See <https://github.com/learnboost/socket.io-node/> for more details.
1439
+ *
1440
+ * @returns {String} Connection url
1441
+ * @api private
1442
+ */
1443
+
1444
+ Transport.prototype.prepareUrl = function () {
1445
+ var options = this.socket.options;
1446
+
1447
+ return this.scheme() + '://'
1448
+ + options.host + ':' + options.port + '/'
1449
+ + options.resource + '/' + io.protocol
1450
+ + '/' + this.name + '/' + this.sessid;
1451
+ };
1452
+
1453
+ /**
1454
+ * Checks if the transport is ready to start a connection.
1455
+ *
1456
+ * @param {Socket} socket The socket instance that needs a transport
1457
+ * @param {Function} fn The callback
1458
+ * @api private
1459
+ */
1460
+
1461
+ Transport.prototype.ready = function (socket, fn) {
1462
+ fn.call(this);
1463
+ };
1464
+ })(
1465
+ 'undefined' != typeof io ? io : module.exports
1466
+ , 'undefined' != typeof io ? io : module.parent.exports
1467
+ );
1468
+
1469
+ /**
1470
+ * socket.io
1471
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
1472
+ * MIT Licensed
1473
+ */
1474
+
1475
+ (function (exports, io, global) {
1476
+
1477
+ /**
1478
+ * Expose constructor.
1479
+ */
1480
+
1481
+ exports.Socket = Socket;
1482
+
1483
+ /**
1484
+ * Create a new `Socket.IO client` which can establish a persistent
1485
+ * connection with a Socket.IO enabled server.
1486
+ *
1487
+ * @api public
1488
+ */
1489
+
1490
+ function Socket (options) {
1491
+ this.options = {
1492
+ port: 80
1493
+ , secure: false
1494
+ , document: 'document' in global ? document : false
1495
+ , resource: 'socket.io'
1496
+ , transports: io.transports
1497
+ , 'connect timeout': 10000
1498
+ , 'try multiple transports': true
1499
+ , 'reconnect': true
1500
+ , 'reconnection delay': 500
1501
+ , 'reconnection limit': Infinity
1502
+ , 'reopen delay': 3000
1503
+ , 'max reconnection attempts': 10
1504
+ , 'sync disconnect on unload': true
1505
+ , 'auto connect': true
1506
+ , 'flash policy port': 10843
1507
+ };
1508
+
1509
+ io.util.merge(this.options, options);
1510
+
1511
+ this.connected = false;
1512
+ this.open = false;
1513
+ this.connecting = false;
1514
+ this.reconnecting = false;
1515
+ this.namespaces = {};
1516
+ this.buffer = [];
1517
+ this.doBuffer = false;
1518
+
1519
+ if (this.options['sync disconnect on unload'] &&
1520
+ (!this.isXDomain() || io.util.ua.hasCORS)) {
1521
+ var self = this;
1522
+
1523
+ io.util.on(global, 'beforeunload', function () {
1524
+ self.disconnectSync();
1525
+ }, false);
1526
+ }
1527
+
1528
+ if (this.options['auto connect']) {
1529
+ this.connect();
1530
+ }
1531
+ };
1532
+
1533
+ /**
1534
+ * Apply EventEmitter mixin.
1535
+ */
1536
+
1537
+ io.util.mixin(Socket, io.EventEmitter);
1538
+
1539
+ /**
1540
+ * Returns a namespace listener/emitter for this socket
1541
+ *
1542
+ * @api public
1543
+ */
1544
+
1545
+ Socket.prototype.of = function (name) {
1546
+ if (!this.namespaces[name]) {
1547
+ this.namespaces[name] = new io.SocketNamespace(this, name);
1548
+
1549
+ if (name !== '') {
1550
+ this.namespaces[name].packet({ type: 'connect' });
1551
+ }
1552
+ }
1553
+
1554
+ return this.namespaces[name];
1555
+ };
1556
+
1557
+ /**
1558
+ * Emits the given event to the Socket and all namespaces
1559
+ *
1560
+ * @api private
1561
+ */
1562
+
1563
+ Socket.prototype.publish = function () {
1564
+ this.emit.apply(this, arguments);
1565
+
1566
+ var nsp;
1567
+
1568
+ for (var i in this.namespaces) {
1569
+ if (this.namespaces.hasOwnProperty(i)) {
1570
+ nsp = this.of(i);
1571
+ nsp.$emit.apply(nsp, arguments);
1572
+ }
1573
+ }
1574
+ };
1575
+
1576
+ /**
1577
+ * Performs the handshake
1578
+ *
1579
+ * @api private
1580
+ */
1581
+
1582
+ function empty () { };
1583
+
1584
+ Socket.prototype.handshake = function (fn) {
1585
+ var self = this
1586
+ , options = this.options;
1587
+
1588
+ function complete (data) {
1589
+ if (data instanceof Error) {
1590
+ self.onError(data.message);
1591
+ } else {
1592
+ fn.apply(null, data.split(':'));
1593
+ }
1594
+ };
1595
+
1596
+ var url = [
1597
+ 'http' + (options.secure ? 's' : '') + ':/'
1598
+ , options.host + ':' + options.port
1599
+ , this.options.resource
1600
+ , io.protocol
1601
+ , io.util.query(this.options.query, 't=' + +new Date)
1602
+ ].join('/');
1603
+
1604
+ if (this.isXDomain()) {
1605
+ var insertAt = document.getElementsByTagName('script')[0]
1606
+ , script = document.createElement('script');
1607
+
1608
+ script.src = url + '&jsonp=' + io.j.length;
1609
+ insertAt.parentNode.insertBefore(script, insertAt);
1610
+
1611
+ io.j.push(function (data) {
1612
+ complete(data);
1613
+ script.parentNode.removeChild(script);
1614
+ });
1615
+ } else {
1616
+ var xhr = io.util.request();
1617
+
1618
+ xhr.open('GET', url, true);
1619
+ xhr.onreadystatechange = function () {
1620
+ if (xhr.readyState == 4) {
1621
+ xhr.onreadystatechange = empty;
1622
+
1623
+ if (xhr.status == 200) {
1624
+ complete(xhr.responseText);
1625
+ } else {
1626
+ !self.reconnecting && self.onError(xhr.responseText);
1627
+ }
1628
+ }
1629
+ };
1630
+ xhr.send(null);
1631
+ }
1632
+ };
1633
+
1634
+ /**
1635
+ * Find an available transport based on the options supplied in the constructor.
1636
+ *
1637
+ * @api private
1638
+ */
1639
+
1640
+ Socket.prototype.getTransport = function (override) {
1641
+ var transports = override || this.transports, match;
1642
+
1643
+ for (var i = 0, transport; transport = transports[i]; i++) {
1644
+ if (io.Transport[transport]
1645
+ && io.Transport[transport].check(this)
1646
+ && (!this.isXDomain() || io.Transport[transport].xdomainCheck())) {
1647
+ return new io.Transport[transport](this, this.sessionid);
1648
+ }
1649
+ }
1650
+
1651
+ return null;
1652
+ };
1653
+
1654
+ /**
1655
+ * Connects to the server.
1656
+ *
1657
+ * @param {Function} [fn] Callback.
1658
+ * @returns {io.Socket}
1659
+ * @api public
1660
+ */
1661
+
1662
+ Socket.prototype.connect = function (fn) {
1663
+ if (this.connecting) {
1664
+ return this;
1665
+ }
1666
+
1667
+ var self = this;
1668
+
1669
+ this.handshake(function (sid, heartbeat, close, transports) {
1670
+ self.sessionid = sid;
1671
+ self.closeTimeout = close * 1000;
1672
+ self.heartbeatTimeout = heartbeat * 1000;
1673
+ self.transports = io.util.intersect(
1674
+ transports.split(',')
1675
+ , self.options.transports
1676
+ );
1677
+
1678
+ function connect (transports){
1679
+ if (self.transport) self.transport.clearTimeouts();
1680
+
1681
+ self.transport = self.getTransport(transports);
1682
+ if (!self.transport) return self.publish('connect_failed');
1683
+
1684
+ // once the transport is ready
1685
+ self.transport.ready(self, function () {
1686
+ self.connecting = true;
1687
+ self.publish('connecting', self.transport.name);
1688
+ self.transport.open();
1689
+
1690
+ if (self.options['connect timeout']) {
1691
+ self.connectTimeoutTimer = setTimeout(function () {
1692
+ if (!self.connected) {
1693
+ self.connecting = false;
1694
+
1695
+ if (self.options['try multiple transports']) {
1696
+ if (!self.remainingTransports) {
1697
+ self.remainingTransports = self.transports.slice(0);
1698
+ }
1699
+
1700
+ var remaining = self.remainingTransports;
1701
+
1702
+ while (remaining.length > 0 && remaining.splice(0,1)[0] !=
1703
+ self.transport.name) {}
1704
+
1705
+ if (remaining.length){
1706
+ connect(remaining);
1707
+ } else {
1708
+ self.publish('connect_failed');
1709
+ }
1710
+ }
1711
+ }
1712
+ }, self.options['connect timeout']);
1713
+ }
1714
+ });
1715
+ }
1716
+
1717
+ connect();
1718
+
1719
+ self.once('connect', function (){
1720
+ clearTimeout(self.connectTimeoutTimer);
1721
+
1722
+ fn && typeof fn == 'function' && fn();
1723
+ });
1724
+ });
1725
+
1726
+ return this;
1727
+ };
1728
+
1729
+ /**
1730
+ * Sends a message.
1731
+ *
1732
+ * @param {Object} data packet.
1733
+ * @returns {io.Socket}
1734
+ * @api public
1735
+ */
1736
+
1737
+ Socket.prototype.packet = function (data) {
1738
+ if (this.connected && !this.doBuffer) {
1739
+ this.transport.packet(data);
1740
+ } else {
1741
+ this.buffer.push(data);
1742
+ }
1743
+
1744
+ return this;
1745
+ };
1746
+
1747
+ /**
1748
+ * Sets buffer state
1749
+ *
1750
+ * @api private
1751
+ */
1752
+
1753
+ Socket.prototype.setBuffer = function (v) {
1754
+ this.doBuffer = v;
1755
+
1756
+ if (!v && this.connected && this.buffer.length) {
1757
+ this.transport.payload(this.buffer);
1758
+ this.buffer = [];
1759
+ }
1760
+ };
1761
+
1762
+ /**
1763
+ * Disconnect the established connect.
1764
+ *
1765
+ * @returns {io.Socket}
1766
+ * @api public
1767
+ */
1768
+
1769
+ Socket.prototype.disconnect = function () {
1770
+ if (this.connected) {
1771
+ if (this.open) {
1772
+ this.of('').packet({ type: 'disconnect' });
1773
+ }
1774
+
1775
+ // handle disconnection immediately
1776
+ this.onDisconnect('booted');
1777
+ }
1778
+
1779
+ return this;
1780
+ };
1781
+
1782
+ /**
1783
+ * Disconnects the socket with a sync XHR.
1784
+ *
1785
+ * @api private
1786
+ */
1787
+
1788
+ Socket.prototype.disconnectSync = function () {
1789
+ // ensure disconnection
1790
+ var xhr = io.util.request()
1791
+ , uri = this.resource + '/' + io.protocol + '/' + this.sessionid;
1792
+
1793
+ xhr.open('GET', uri, true);
1794
+
1795
+ // handle disconnection immediately
1796
+ this.onDisconnect('booted');
1797
+ };
1798
+
1799
+ /**
1800
+ * Check if we need to use cross domain enabled transports. Cross domain would
1801
+ * be a different port or different domain name.
1802
+ *
1803
+ * @returns {Boolean}
1804
+ * @api private
1805
+ */
1806
+
1807
+ Socket.prototype.isXDomain = function () {
1808
+
1809
+ var port = window.location.port ||
1810
+ ('https:' == window.location.protocol ? 443 : 80);
1811
+
1812
+ return this.options.host !== document.domain || this.options.port != port;
1813
+ };
1814
+
1815
+ /**
1816
+ * Called upon handshake.
1817
+ *
1818
+ * @api private
1819
+ */
1820
+
1821
+ Socket.prototype.onConnect = function () {
1822
+ if (!this.connected) {
1823
+ this.connected = true;
1824
+ this.connecting = false;
1825
+ if (!this.doBuffer) {
1826
+ // make sure to flush the buffer
1827
+ this.setBuffer(false);
1828
+ }
1829
+ this.emit('connect');
1830
+ }
1831
+ };
1832
+
1833
+ /**
1834
+ * Called when the transport opens
1835
+ *
1836
+ * @api private
1837
+ */
1838
+
1839
+ Socket.prototype.onOpen = function () {
1840
+ this.open = true;
1841
+ };
1842
+
1843
+ /**
1844
+ * Called when the transport closes.
1845
+ *
1846
+ * @api private
1847
+ */
1848
+
1849
+ Socket.prototype.onClose = function () {
1850
+ this.open = false;
1851
+ };
1852
+
1853
+ /**
1854
+ * Called when the transport first opens a connection
1855
+ *
1856
+ * @param text
1857
+ */
1858
+
1859
+ Socket.prototype.onPacket = function (packet) {
1860
+ this.of(packet.endpoint).onPacket(packet);
1861
+ };
1862
+
1863
+ /**
1864
+ * Handles an error.
1865
+ *
1866
+ * @api private
1867
+ */
1868
+
1869
+ Socket.prototype.onError = function (err) {
1870
+ if (err && err.advice) {
1871
+ if (err.advice === 'reconnect' && this.connected) {
1872
+ this.disconnect();
1873
+ this.reconnect();
1874
+ }
1875
+ }
1876
+
1877
+ this.publish('error', err && err.reason ? err.reason : err);
1878
+ };
1879
+
1880
+ /**
1881
+ * Called when the transport disconnects.
1882
+ *
1883
+ * @api private
1884
+ */
1885
+
1886
+ Socket.prototype.onDisconnect = function (reason) {
1887
+ var wasConnected = this.connected;
1888
+
1889
+ this.connected = false;
1890
+ this.connecting = false;
1891
+ this.open = false;
1892
+
1893
+ if (wasConnected) {
1894
+ this.transport.close();
1895
+ this.transport.clearTimeouts();
1896
+ this.publish('disconnect', reason);
1897
+
1898
+ if ('booted' != reason && this.options.reconnect && !this.reconnecting) {
1899
+ this.reconnect();
1900
+ }
1901
+ }
1902
+ };
1903
+
1904
+ /**
1905
+ * Called upon reconnection.
1906
+ *
1907
+ * @api private
1908
+ */
1909
+
1910
+ Socket.prototype.reconnect = function () {
1911
+ this.reconnecting = true;
1912
+ this.reconnectionAttempts = 0;
1913
+ this.reconnectionDelay = this.options['reconnection delay'];
1914
+
1915
+ var self = this
1916
+ , maxAttempts = this.options['max reconnection attempts']
1917
+ , tryMultiple = this.options['try multiple transports']
1918
+ , limit = this.options['reconnection limit'];
1919
+
1920
+ function reset () {
1921
+ if (self.connected) {
1922
+ for (var i in self.namespaces) {
1923
+ if (self.namespaces.hasOwnProperty(i) && '' !== i) {
1924
+ self.namespaces[i].packet({ type: 'connect' });
1925
+ }
1926
+ }
1927
+ self.publish('reconnect', self.transport.name, self.reconnectionAttempts);
1928
+ }
1929
+
1930
+ self.removeListener('connect_failed', maybeReconnect);
1931
+ self.removeListener('connect', maybeReconnect);
1932
+
1933
+ self.reconnecting = false;
1934
+
1935
+ delete self.reconnectionAttempts;
1936
+ delete self.reconnectionDelay;
1937
+ delete self.reconnectionTimer;
1938
+ delete self.redoTransports;
1939
+
1940
+ self.options['try multiple transports'] = tryMultiple;
1941
+ };
1942
+
1943
+ function maybeReconnect () {
1944
+ if (!self.reconnecting) {
1945
+ return;
1946
+ }
1947
+
1948
+ if (self.connected) {
1949
+ return reset();
1950
+ };
1951
+
1952
+ if (self.connecting && self.reconnecting) {
1953
+ return self.reconnectionTimer = setTimeout(maybeReconnect, 1000);
1954
+ }
1955
+
1956
+ if (self.reconnectionAttempts++ >= maxAttempts) {
1957
+ if (!self.redoTransports) {
1958
+ self.on('connect_failed', maybeReconnect);
1959
+ self.options['try multiple transports'] = true;
1960
+ self.transport = self.getTransport();
1961
+ self.redoTransports = true;
1962
+ self.connect();
1963
+ } else {
1964
+ self.publish('reconnect_failed');
1965
+ reset();
1966
+ }
1967
+ } else {
1968
+ if (self.reconnectionDelay < limit) {
1969
+ self.reconnectionDelay *= 2; // exponential back off
1970
+ }
1971
+
1972
+ self.connect();
1973
+ self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts);
1974
+ self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay);
1975
+ }
1976
+ };
1977
+
1978
+ this.options['try multiple transports'] = false;
1979
+ this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay);
1980
+
1981
+ this.on('connect', maybeReconnect);
1982
+ };
1983
+
1984
+ })(
1985
+ 'undefined' != typeof io ? io : module.exports
1986
+ , 'undefined' != typeof io ? io : module.parent.exports
1987
+ , this
1988
+ );
1989
+ /**
1990
+ * socket.io
1991
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
1992
+ * MIT Licensed
1993
+ */
1994
+
1995
+ (function (exports, io) {
1996
+
1997
+ /**
1998
+ * Expose constructor.
1999
+ */
2000
+
2001
+ exports.SocketNamespace = SocketNamespace;
2002
+
2003
+ /**
2004
+ * Socket namespace constructor.
2005
+ *
2006
+ * @constructor
2007
+ * @api public
2008
+ */
2009
+
2010
+ function SocketNamespace (socket, name) {
2011
+ this.socket = socket;
2012
+ this.name = name || '';
2013
+ this.flags = {};
2014
+ this.json = new Flag(this, 'json');
2015
+ this.ackPackets = 0;
2016
+ this.acks = {};
2017
+ };
2018
+
2019
+ /**
2020
+ * Apply EventEmitter mixin.
2021
+ */
2022
+
2023
+ io.util.mixin(SocketNamespace, io.EventEmitter);
2024
+
2025
+ /**
2026
+ * Copies emit since we override it
2027
+ *
2028
+ * @api private
2029
+ */
2030
+
2031
+ SocketNamespace.prototype.$emit = io.EventEmitter.prototype.emit;
2032
+
2033
+ /**
2034
+ * Creates a new namespace, by proxying the request to the socket. This
2035
+ * allows us to use the synax as we do on the server.
2036
+ *
2037
+ * @api public
2038
+ */
2039
+
2040
+ SocketNamespace.prototype.of = function () {
2041
+ return this.socket.of.apply(this.socket, arguments);
2042
+ };
2043
+
2044
+ /**
2045
+ * Sends a packet.
2046
+ *
2047
+ * @api private
2048
+ */
2049
+
2050
+ SocketNamespace.prototype.packet = function (packet) {
2051
+ packet.endpoint = this.name;
2052
+ this.socket.packet(packet);
2053
+ this.flags = {};
2054
+ return this;
2055
+ };
2056
+
2057
+ /**
2058
+ * Sends a message
2059
+ *
2060
+ * @api public
2061
+ */
2062
+
2063
+ SocketNamespace.prototype.send = function (data, fn) {
2064
+ var packet = {
2065
+ type: this.flags.json ? 'json' : 'message'
2066
+ , data: data
2067
+ };
2068
+
2069
+ if ('function' == typeof fn) {
2070
+ packet.id = ++this.ackPackets;
2071
+ packet.ack = true;
2072
+ this.acks[packet.id] = fn;
2073
+ }
2074
+
2075
+ return this.packet(packet);
2076
+ };
2077
+
2078
+ /**
2079
+ * Emits an event
2080
+ *
2081
+ * @api public
2082
+ */
2083
+
2084
+ SocketNamespace.prototype.emit = function (name) {
2085
+ var args = Array.prototype.slice.call(arguments, 1)
2086
+ , lastArg = args[args.length - 1]
2087
+ , packet = {
2088
+ type: 'event'
2089
+ , name: name
2090
+ };
2091
+
2092
+ if ('function' == typeof lastArg) {
2093
+ packet.id = ++this.ackPackets;
2094
+ packet.ack = 'data';
2095
+ this.acks[packet.id] = lastArg;
2096
+ args = args.slice(0, args.length - 1);
2097
+ }
2098
+
2099
+ packet.args = args;
2100
+
2101
+ return this.packet(packet);
2102
+ };
2103
+
2104
+ /**
2105
+ * Disconnects the namespace
2106
+ *
2107
+ * @api private
2108
+ */
2109
+
2110
+ SocketNamespace.prototype.disconnect = function () {
2111
+ if (this.name === '') {
2112
+ this.socket.disconnect();
2113
+ } else {
2114
+ this.packet({ type: 'disconnect' });
2115
+ this.$emit('disconnect');
2116
+ }
2117
+
2118
+ return this;
2119
+ };
2120
+
2121
+ /**
2122
+ * Handles a packet
2123
+ *
2124
+ * @api private
2125
+ */
2126
+
2127
+ SocketNamespace.prototype.onPacket = function (packet) {
2128
+ var self = this;
2129
+
2130
+ function ack () {
2131
+ self.packet({
2132
+ type: 'ack'
2133
+ , args: io.util.toArray(arguments)
2134
+ , ackId: packet.id
2135
+ });
2136
+ };
2137
+
2138
+ switch (packet.type) {
2139
+ case 'connect':
2140
+ this.$emit('connect');
2141
+ break;
2142
+
2143
+ case 'disconnect':
2144
+ if (this.name === '') {
2145
+ this.socket.onDisconnect(packet.reason || 'booted');
2146
+ } else {
2147
+ this.$emit('disconnect', packet.reason);
2148
+ }
2149
+ break;
2150
+
2151
+ case 'message':
2152
+ case 'json':
2153
+ var params = ['message', packet.data];
2154
+
2155
+ if (packet.ack == 'data') {
2156
+ params.push(ack);
2157
+ } else if (packet.ack) {
2158
+ this.packet({ type: 'ack', ackId: packet.id });
2159
+ }
2160
+
2161
+ this.$emit.apply(this, params);
2162
+ break;
2163
+
2164
+ case 'event':
2165
+ var params = [packet.name].concat(packet.args);
2166
+
2167
+ if (packet.ack == 'data')
2168
+ params.push(ack);
2169
+
2170
+ this.$emit.apply(this, params);
2171
+ break;
2172
+
2173
+ case 'ack':
2174
+ if (this.acks[packet.ackId]) {
2175
+ this.acks[packet.ackId].apply(this, packet.args);
2176
+ delete this.acks[packet.ackId];
2177
+ }
2178
+ break;
2179
+
2180
+ case 'error':
2181
+ if (packet.advice){
2182
+ this.socket.onError(packet);
2183
+ } else {
2184
+ if (packet.reason == 'unauthorized') {
2185
+ this.$emit('connect_failed', packet.reason);
2186
+ } else {
2187
+ this.$emit('error', packet.reason);
2188
+ }
2189
+ }
2190
+ break;
2191
+ }
2192
+ };
2193
+
2194
+ /**
2195
+ * Flag interface.
2196
+ *
2197
+ * @api private
2198
+ */
2199
+
2200
+ function Flag (nsp, name) {
2201
+ this.namespace = nsp;
2202
+ this.name = name;
2203
+ };
2204
+
2205
+ /**
2206
+ * Send a message
2207
+ *
2208
+ * @api public
2209
+ */
2210
+
2211
+ Flag.prototype.send = function () {
2212
+ this.namespace.flags[this.name] = true;
2213
+ this.namespace.send.apply(this.namespace, arguments);
2214
+ };
2215
+
2216
+ /**
2217
+ * Emit an event
2218
+ *
2219
+ * @api public
2220
+ */
2221
+
2222
+ Flag.prototype.emit = function () {
2223
+ this.namespace.flags[this.name] = true;
2224
+ this.namespace.emit.apply(this.namespace, arguments);
2225
+ };
2226
+
2227
+ })(
2228
+ 'undefined' != typeof io ? io : module.exports
2229
+ , 'undefined' != typeof io ? io : module.parent.exports
2230
+ );
2231
+
2232
+ /**
2233
+ * socket.io
2234
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
2235
+ * MIT Licensed
2236
+ */
2237
+
2238
+ (function (exports, io) {
2239
+
2240
+ /**
2241
+ * Expose constructor.
2242
+ */
2243
+
2244
+ exports.websocket = WS;
2245
+
2246
+ /**
2247
+ * The WebSocket transport uses the HTML5 WebSocket API to establish an
2248
+ * persistent connection with the Socket.IO server. This transport will also
2249
+ * be inherited by the FlashSocket fallback as it provides a API compatible
2250
+ * polyfill for the WebSockets.
2251
+ *
2252
+ * @constructor
2253
+ * @extends {io.Transport}
2254
+ * @api public
2255
+ */
2256
+
2257
+ function WS (socket) {
2258
+ io.Transport.apply(this, arguments);
2259
+ };
2260
+
2261
+ /**
2262
+ * Inherits from Transport.
2263
+ */
2264
+
2265
+ io.util.inherit(WS, io.Transport);
2266
+
2267
+ /**
2268
+ * Transport name
2269
+ *
2270
+ * @api public
2271
+ */
2272
+
2273
+ WS.prototype.name = 'websocket';
2274
+
2275
+ /**
2276
+ * Initializes a new `WebSocket` connection with the Socket.IO server. We attach
2277
+ * all the appropriate listeners to handle the responses from the server.
2278
+ *
2279
+ * @returns {Transport}
2280
+ * @api public
2281
+ */
2282
+
2283
+ WS.prototype.open = function () {
2284
+ var query = io.util.query(this.socket.options.query)
2285
+ , self = this
2286
+ , Socket
2287
+
2288
+
2289
+ if (!Socket) {
2290
+ Socket = window.MozWebSocket || window.WebSocket;
2291
+ }
2292
+
2293
+ this.websocket = new Socket(this.prepareUrl() + query);
2294
+
2295
+ this.websocket.onopen = function () {
2296
+ self.onOpen();
2297
+ self.socket.setBuffer(false);
2298
+ };
2299
+ this.websocket.onmessage = function (ev) {
2300
+ self.onData(ev.data);
2301
+ };
2302
+ this.websocket.onclose = function () {
2303
+ self.onClose();
2304
+ self.socket.setBuffer(true);
2305
+ };
2306
+ this.websocket.onerror = function (e) {
2307
+ self.onError(e);
2308
+ };
2309
+
2310
+ return this;
2311
+ };
2312
+
2313
+ /**
2314
+ * Send a message to the Socket.IO server. The message will automatically be
2315
+ * encoded in the correct message format.
2316
+ *
2317
+ * @returns {Transport}
2318
+ * @api public
2319
+ */
2320
+
2321
+ WS.prototype.send = function (data) {
2322
+ this.websocket.send(data);
2323
+ return this;
2324
+ };
2325
+
2326
+ /**
2327
+ * Payload
2328
+ *
2329
+ * @api private
2330
+ */
2331
+
2332
+ WS.prototype.payload = function (arr) {
2333
+ for (var i = 0, l = arr.length; i < l; i++) {
2334
+ this.packet(arr[i]);
2335
+ }
2336
+ return this;
2337
+ };
2338
+
2339
+ /**
2340
+ * Disconnect the established `WebSocket` connection.
2341
+ *
2342
+ * @returns {Transport}
2343
+ * @api public
2344
+ */
2345
+
2346
+ WS.prototype.close = function () {
2347
+ this.websocket.close();
2348
+ return this;
2349
+ };
2350
+
2351
+ /**
2352
+ * Handle the errors that `WebSocket` might be giving when we
2353
+ * are attempting to connect or send messages.
2354
+ *
2355
+ * @param {Error} e The error.
2356
+ * @api private
2357
+ */
2358
+
2359
+ WS.prototype.onError = function (e) {
2360
+ this.socket.onError(e);
2361
+ };
2362
+
2363
+ /**
2364
+ * Returns the appropriate scheme for the URI generation.
2365
+ *
2366
+ * @api private
2367
+ */
2368
+ WS.prototype.scheme = function () {
2369
+ return this.socket.options.secure ? 'wss' : 'ws';
2370
+ };
2371
+
2372
+ /**
2373
+ * Checks if the browser has support for native `WebSockets` and that
2374
+ * it's not the polyfill created for the FlashSocket transport.
2375
+ *
2376
+ * @return {Boolean}
2377
+ * @api public
2378
+ */
2379
+
2380
+ WS.check = function () {
2381
+ return ('WebSocket' in window && !('__addTask' in WebSocket))
2382
+ || 'MozWebSocket' in window;
2383
+ };
2384
+
2385
+ /**
2386
+ * Check if the `WebSocket` transport support cross domain communications.
2387
+ *
2388
+ * @returns {Boolean}
2389
+ * @api public
2390
+ */
2391
+
2392
+ WS.xdomainCheck = function () {
2393
+ return true;
2394
+ };
2395
+
2396
+ /**
2397
+ * Add the transport to your public io.transports array.
2398
+ *
2399
+ * @api private
2400
+ */
2401
+
2402
+ io.transports.push('websocket');
2403
+
2404
+ })(
2405
+ 'undefined' != typeof io ? io.Transport : module.exports
2406
+ , 'undefined' != typeof io ? io : module.parent.exports
2407
+ );
2408
+
2409
+ /**
2410
+ * socket.io
2411
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
2412
+ * MIT Licensed
2413
+ */
2414
+
2415
+ (function (exports, io) {
2416
+
2417
+ /**
2418
+ * Expose constructor.
2419
+ */
2420
+
2421
+ exports.flashsocket = Flashsocket;
2422
+
2423
+ /**
2424
+ * The FlashSocket transport. This is a API wrapper for the HTML5 WebSocket
2425
+ * specification. It uses a .swf file to communicate with the server. If you want
2426
+ * to serve the .swf file from a other server than where the Socket.IO script is
2427
+ * coming from you need to use the insecure version of the .swf. More information
2428
+ * about this can be found on the github page.
2429
+ *
2430
+ * @constructor
2431
+ * @extends {io.Transport.websocket}
2432
+ * @api public
2433
+ */
2434
+
2435
+ function Flashsocket () {
2436
+ io.Transport.websocket.apply(this, arguments);
2437
+ };
2438
+
2439
+ /**
2440
+ * Inherits from Transport.
2441
+ */
2442
+
2443
+ io.util.inherit(Flashsocket, io.Transport.websocket);
2444
+
2445
+ /**
2446
+ * Transport name
2447
+ *
2448
+ * @api public
2449
+ */
2450
+
2451
+ Flashsocket.prototype.name = 'flashsocket';
2452
+
2453
+ /**
2454
+ * Disconnect the established `FlashSocket` connection. This is done by adding a
2455
+ * new task to the FlashSocket. The rest will be handled off by the `WebSocket`
2456
+ * transport.
2457
+ *
2458
+ * @returns {Transport}
2459
+ * @api public
2460
+ */
2461
+
2462
+ Flashsocket.prototype.open = function () {
2463
+ var self = this
2464
+ , args = arguments;
2465
+
2466
+ WebSocket.__addTask(function () {
2467
+ io.Transport.websocket.prototype.open.apply(self, args);
2468
+ });
2469
+ return this;
2470
+ };
2471
+
2472
+ /**
2473
+ * Sends a message to the Socket.IO server. This is done by adding a new
2474
+ * task to the FlashSocket. The rest will be handled off by the `WebSocket`
2475
+ * transport.
2476
+ *
2477
+ * @returns {Transport}
2478
+ * @api public
2479
+ */
2480
+
2481
+ Flashsocket.prototype.send = function () {
2482
+ var self = this, args = arguments;
2483
+ WebSocket.__addTask(function () {
2484
+ io.Transport.websocket.prototype.send.apply(self, args);
2485
+ });
2486
+ return this;
2487
+ };
2488
+
2489
+ /**
2490
+ * Disconnects the established `FlashSocket` connection.
2491
+ *
2492
+ * @returns {Transport}
2493
+ * @api public
2494
+ */
2495
+
2496
+ Flashsocket.prototype.close = function () {
2497
+ WebSocket.__tasks.length = 0;
2498
+ io.Transport.websocket.prototype.close.call(this);
2499
+ return this;
2500
+ };
2501
+
2502
+ /**
2503
+ * The WebSocket fall back needs to append the flash container to the body
2504
+ * element, so we need to make sure we have access to it. Or defer the call
2505
+ * until we are sure there is a body element.
2506
+ *
2507
+ * @param {Socket} socket The socket instance that needs a transport
2508
+ * @param {Function} fn The callback
2509
+ * @api private
2510
+ */
2511
+
2512
+ Flashsocket.prototype.ready = function (socket, fn) {
2513
+ function init () {
2514
+ var options = socket.options
2515
+ , port = options['flash policy port']
2516
+ , path = [
2517
+ 'http' + (options.secure ? 's' : '') + ':/'
2518
+ , options.host + ':' + options.port
2519
+ , options.resource
2520
+ , 'static/flashsocket'
2521
+ , 'WebSocketMain' + (socket.isXDomain() ? 'Insecure' : '') + '.swf'
2522
+ ];
2523
+
2524
+ // Only start downloading the swf file when the checked that this browser
2525
+ // actually supports it
2526
+ if (!Flashsocket.loaded) {
2527
+ if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined') {
2528
+ // Set the correct file based on the XDomain settings
2529
+ WEB_SOCKET_SWF_LOCATION = path.join('/');
2530
+ }
2531
+
2532
+ if (port !== 843) {
2533
+ WebSocket.loadFlashPolicyFile('xmlsocket://' + options.host + ':' + port);
2534
+ }
2535
+
2536
+ WebSocket.__initialize();
2537
+ Flashsocket.loaded = true;
2538
+ }
2539
+
2540
+ fn.call(self);
2541
+ }
2542
+
2543
+ var self = this;
2544
+ if (document.body) return init();
2545
+
2546
+ io.util.load(init);
2547
+ };
2548
+
2549
+ /**
2550
+ * Check if the FlashSocket transport is supported as it requires that the Adobe
2551
+ * Flash Player plug-in version `10.0.0` or greater is installed. And also check if
2552
+ * the polyfill is correctly loaded.
2553
+ *
2554
+ * @returns {Boolean}
2555
+ * @api public
2556
+ */
2557
+
2558
+ Flashsocket.check = function () {
2559
+ if (
2560
+ typeof WebSocket == 'undefined'
2561
+ || !('__initialize' in WebSocket) || !swfobject
2562
+ ) return false;
2563
+
2564
+ return swfobject.getFlashPlayerVersion().major >= 10;
2565
+ };
2566
+
2567
+ /**
2568
+ * Check if the FlashSocket transport can be used as cross domain / cross origin
2569
+ * transport. Because we can't see which type (secure or insecure) of .swf is used
2570
+ * we will just return true.
2571
+ *
2572
+ * @returns {Boolean}
2573
+ * @api public
2574
+ */
2575
+
2576
+ Flashsocket.xdomainCheck = function () {
2577
+ return true;
2578
+ };
2579
+
2580
+ /**
2581
+ * Disable AUTO_INITIALIZATION
2582
+ */
2583
+
2584
+ if (typeof window != 'undefined') {
2585
+ WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true;
2586
+ }
2587
+
2588
+ /**
2589
+ * Add the transport to your public io.transports array.
2590
+ *
2591
+ * @api private
2592
+ */
2593
+
2594
+ io.transports.push('flashsocket');
2595
+ })(
2596
+ 'undefined' != typeof io ? io.Transport : module.exports
2597
+ , 'undefined' != typeof io ? io : module.parent.exports
2598
+ );
2599
+ /* SWFObject v2.2 <http://code.google.com/p/swfobject/>
2600
+ is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
2601
+ */
2602
+ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
2603
+ // License: New BSD License
2604
+ // Reference: http://dev.w3.org/html5/websockets/
2605
+ // Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
2606
+
2607
+ (function() {
2608
+
2609
+ if (window.WebSocket) return;
2610
+
2611
+ var console = window.console;
2612
+ if (!console || !console.log || !console.error) {
2613
+ console = {log: function(){ }, error: function(){ }};
2614
+ }
2615
+
2616
+ if (!swfobject.hasFlashPlayerVersion("10.0.0")) {
2617
+ console.error("Flash Player >= 10.0.0 is required.");
2618
+ return;
2619
+ }
2620
+ if (location.protocol == "file:") {
2621
+ console.error(
2622
+ "WARNING: web-socket-js doesn't work in file:///... URL " +
2623
+ "unless you set Flash Security Settings properly. " +
2624
+ "Open the page via Web server i.e. http://...");
2625
+ }
2626
+
2627
+ /**
2628
+ * This class represents a faux web socket.
2629
+ * @param {string} url
2630
+ * @param {array or string} protocols
2631
+ * @param {string} proxyHost
2632
+ * @param {int} proxyPort
2633
+ * @param {string} headers
2634
+ */
2635
+ WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
2636
+ var self = this;
2637
+ self.__id = WebSocket.__nextId++;
2638
+ WebSocket.__instances[self.__id] = self;
2639
+ self.readyState = WebSocket.CONNECTING;
2640
+ self.bufferedAmount = 0;
2641
+ self.__events = {};
2642
+ if (!protocols) {
2643
+ protocols = [];
2644
+ } else if (typeof protocols == "string") {
2645
+ protocols = [protocols];
2646
+ }
2647
+ // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
2648
+ // Otherwise, when onopen fires immediately, onopen is called before it is set.
2649
+ setTimeout(function() {
2650
+ WebSocket.__addTask(function() {
2651
+ WebSocket.__flash.create(
2652
+ self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
2653
+ });
2654
+ }, 0);
2655
+ };
2656
+
2657
+ /**
2658
+ * Send data to the web socket.
2659
+ * @param {string} data The data to send to the socket.
2660
+ * @return {boolean} True for success, false for failure.
2661
+ */
2662
+ WebSocket.prototype.send = function(data) {
2663
+ if (this.readyState == WebSocket.CONNECTING) {
2664
+ throw "INVALID_STATE_ERR: Web Socket connection has not been established";
2665
+ }
2666
+ // We use encodeURIComponent() here, because FABridge doesn't work if
2667
+ // the argument includes some characters. We don't use escape() here
2668
+ // because of this:
2669
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
2670
+ // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
2671
+ // preserve all Unicode characters either e.g. "\uffff" in Firefox.
2672
+ // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require
2673
+ // additional testing.
2674
+ var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
2675
+ if (result < 0) { // success
2676
+ return true;
2677
+ } else {
2678
+ this.bufferedAmount += result;
2679
+ return false;
2680
+ }
2681
+ };
2682
+
2683
+ /**
2684
+ * Close this web socket gracefully.
2685
+ */
2686
+ WebSocket.prototype.close = function() {
2687
+ if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
2688
+ return;
2689
+ }
2690
+ this.readyState = WebSocket.CLOSING;
2691
+ WebSocket.__flash.close(this.__id);
2692
+ };
2693
+
2694
+ /**
2695
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
2696
+ *
2697
+ * @param {string} type
2698
+ * @param {function} listener
2699
+ * @param {boolean} useCapture
2700
+ * @return void
2701
+ */
2702
+ WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
2703
+ if (!(type in this.__events)) {
2704
+ this.__events[type] = [];
2705
+ }
2706
+ this.__events[type].push(listener);
2707
+ };
2708
+
2709
+ /**
2710
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
2711
+ *
2712
+ * @param {string} type
2713
+ * @param {function} listener
2714
+ * @param {boolean} useCapture
2715
+ * @return void
2716
+ */
2717
+ WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
2718
+ if (!(type in this.__events)) return;
2719
+ var events = this.__events[type];
2720
+ for (var i = events.length - 1; i >= 0; --i) {
2721
+ if (events[i] === listener) {
2722
+ events.splice(i, 1);
2723
+ break;
2724
+ }
2725
+ }
2726
+ };
2727
+
2728
+ /**
2729
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
2730
+ *
2731
+ * @param {Event} event
2732
+ * @return void
2733
+ */
2734
+ WebSocket.prototype.dispatchEvent = function(event) {
2735
+ var events = this.__events[event.type] || [];
2736
+ for (var i = 0; i < events.length; ++i) {
2737
+ events[i](event);
2738
+ }
2739
+ var handler = this["on" + event.type];
2740
+ if (handler) handler(event);
2741
+ };
2742
+
2743
+ /**
2744
+ * Handles an event from Flash.
2745
+ * @param {Object} flashEvent
2746
+ */
2747
+ WebSocket.prototype.__handleEvent = function(flashEvent) {
2748
+ if ("readyState" in flashEvent) {
2749
+ this.readyState = flashEvent.readyState;
2750
+ }
2751
+ if ("protocol" in flashEvent) {
2752
+ this.protocol = flashEvent.protocol;
2753
+ }
2754
+
2755
+ var jsEvent;
2756
+ if (flashEvent.type == "open" || flashEvent.type == "error") {
2757
+ jsEvent = this.__createSimpleEvent(flashEvent.type);
2758
+ } else if (flashEvent.type == "close") {
2759
+ // TODO implement jsEvent.wasClean
2760
+ jsEvent = this.__createSimpleEvent("close");
2761
+ } else if (flashEvent.type == "message") {
2762
+ var data = decodeURIComponent(flashEvent.message);
2763
+ jsEvent = this.__createMessageEvent("message", data);
2764
+ } else {
2765
+ throw "unknown event type: " + flashEvent.type;
2766
+ }
2767
+
2768
+ this.dispatchEvent(jsEvent);
2769
+ };
2770
+
2771
+ WebSocket.prototype.__createSimpleEvent = function(type) {
2772
+ if (document.createEvent && window.Event) {
2773
+ var event = document.createEvent("Event");
2774
+ event.initEvent(type, false, false);
2775
+ return event;
2776
+ } else {
2777
+ return {type: type, bubbles: false, cancelable: false};
2778
+ }
2779
+ };
2780
+
2781
+ WebSocket.prototype.__createMessageEvent = function(type, data) {
2782
+ if (document.createEvent && window.MessageEvent && !window.opera) {
2783
+ var event = document.createEvent("MessageEvent");
2784
+ event.initMessageEvent("message", false, false, data, null, null, window, null);
2785
+ return event;
2786
+ } else {
2787
+ // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
2788
+ return {type: type, data: data, bubbles: false, cancelable: false};
2789
+ }
2790
+ };
2791
+
2792
+ /**
2793
+ * Define the WebSocket readyState enumeration.
2794
+ */
2795
+ WebSocket.CONNECTING = 0;
2796
+ WebSocket.OPEN = 1;
2797
+ WebSocket.CLOSING = 2;
2798
+ WebSocket.CLOSED = 3;
2799
+
2800
+ WebSocket.__flash = null;
2801
+ WebSocket.__instances = {};
2802
+ WebSocket.__tasks = [];
2803
+ WebSocket.__nextId = 0;
2804
+
2805
+ /**
2806
+ * Load a new flash security policy file.
2807
+ * @param {string} url
2808
+ */
2809
+ WebSocket.loadFlashPolicyFile = function(url){
2810
+ WebSocket.__addTask(function() {
2811
+ WebSocket.__flash.loadManualPolicyFile(url);
2812
+ });
2813
+ };
2814
+
2815
+ /**
2816
+ * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
2817
+ */
2818
+ WebSocket.__initialize = function() {
2819
+ if (WebSocket.__flash) return;
2820
+
2821
+ if (WebSocket.__swfLocation) {
2822
+ // For backword compatibility.
2823
+ window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
2824
+ }
2825
+ if (!window.WEB_SOCKET_SWF_LOCATION) {
2826
+ console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
2827
+ return;
2828
+ }
2829
+ var container = document.createElement("div");
2830
+ container.id = "webSocketContainer";
2831
+ // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
2832
+ // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
2833
+ // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
2834
+ // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
2835
+ // the best we can do as far as we know now.
2836
+ container.style.position = "absolute";
2837
+ if (WebSocket.__isFlashLite()) {
2838
+ container.style.left = "0px";
2839
+ container.style.top = "0px";
2840
+ } else {
2841
+ container.style.left = "-100px";
2842
+ container.style.top = "-100px";
2843
+ }
2844
+ var holder = document.createElement("div");
2845
+ holder.id = "webSocketFlash";
2846
+ container.appendChild(holder);
2847
+ document.body.appendChild(container);
2848
+ // See this article for hasPriority:
2849
+ // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
2850
+ swfobject.embedSWF(
2851
+ WEB_SOCKET_SWF_LOCATION,
2852
+ "webSocketFlash",
2853
+ "1" /* width */,
2854
+ "1" /* height */,
2855
+ "10.0.0" /* SWF version */,
2856
+ null,
2857
+ null,
2858
+ {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
2859
+ null,
2860
+ function(e) {
2861
+ if (!e.success) {
2862
+ console.error("[WebSocket] swfobject.embedSWF failed");
2863
+ }
2864
+ });
2865
+ };
2866
+
2867
+ /**
2868
+ * Called by Flash to notify JS that it's fully loaded and ready
2869
+ * for communication.
2870
+ */
2871
+ WebSocket.__onFlashInitialized = function() {
2872
+ // We need to set a timeout here to avoid round-trip calls
2873
+ // to flash during the initialization process.
2874
+ setTimeout(function() {
2875
+ WebSocket.__flash = document.getElementById("webSocketFlash");
2876
+ WebSocket.__flash.setCallerUrl(location.href);
2877
+ WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
2878
+ for (var i = 0; i < WebSocket.__tasks.length; ++i) {
2879
+ WebSocket.__tasks[i]();
2880
+ }
2881
+ WebSocket.__tasks = [];
2882
+ }, 0);
2883
+ };
2884
+
2885
+ /**
2886
+ * Called by Flash to notify WebSockets events are fired.
2887
+ */
2888
+ WebSocket.__onFlashEvent = function() {
2889
+ setTimeout(function() {
2890
+ try {
2891
+ // Gets events using receiveEvents() instead of getting it from event object
2892
+ // of Flash event. This is to make sure to keep message order.
2893
+ // It seems sometimes Flash events don't arrive in the same order as they are sent.
2894
+ var events = WebSocket.__flash.receiveEvents();
2895
+ for (var i = 0; i < events.length; ++i) {
2896
+ WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
2897
+ }
2898
+ } catch (e) {
2899
+ console.error(e);
2900
+ }
2901
+ }, 0);
2902
+ return true;
2903
+ };
2904
+
2905
+ // Called by Flash.
2906
+ WebSocket.__log = function(message) {
2907
+ console.log(decodeURIComponent(message));
2908
+ };
2909
+
2910
+ // Called by Flash.
2911
+ WebSocket.__error = function(message) {
2912
+ console.error(decodeURIComponent(message));
2913
+ };
2914
+
2915
+ WebSocket.__addTask = function(task) {
2916
+ if (WebSocket.__flash) {
2917
+ task();
2918
+ } else {
2919
+ WebSocket.__tasks.push(task);
2920
+ }
2921
+ };
2922
+
2923
+ /**
2924
+ * Test if the browser is running flash lite.
2925
+ * @return {boolean} True if flash lite is running, false otherwise.
2926
+ */
2927
+ WebSocket.__isFlashLite = function() {
2928
+ if (!window.navigator || !window.navigator.mimeTypes) {
2929
+ return false;
2930
+ }
2931
+ var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
2932
+ if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
2933
+ return false;
2934
+ }
2935
+ return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
2936
+ };
2937
+
2938
+ if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
2939
+ if (window.addEventListener) {
2940
+ window.addEventListener("load", function(){
2941
+ WebSocket.__initialize();
2942
+ }, false);
2943
+ } else {
2944
+ window.attachEvent("onload", function(){
2945
+ WebSocket.__initialize();
2946
+ });
2947
+ }
2948
+ }
2949
+
2950
+ })();
2951
+
2952
+ /**
2953
+ * socket.io
2954
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
2955
+ * MIT Licensed
2956
+ */
2957
+
2958
+ (function (exports, io, global) {
2959
+
2960
+ /**
2961
+ * Expose constructor.
2962
+ *
2963
+ * @api public
2964
+ */
2965
+
2966
+ exports.XHR = XHR;
2967
+
2968
+ /**
2969
+ * XHR constructor
2970
+ *
2971
+ * @costructor
2972
+ * @api public
2973
+ */
2974
+
2975
+ function XHR (socket) {
2976
+ if (!socket) return;
2977
+
2978
+ io.Transport.apply(this, arguments);
2979
+ this.sendBuffer = [];
2980
+ };
2981
+
2982
+ /**
2983
+ * Inherits from Transport.
2984
+ */
2985
+
2986
+ io.util.inherit(XHR, io.Transport);
2987
+
2988
+ /**
2989
+ * Establish a connection
2990
+ *
2991
+ * @returns {Transport}
2992
+ * @api public
2993
+ */
2994
+
2995
+ XHR.prototype.open = function () {
2996
+ this.socket.setBuffer(false);
2997
+ this.onOpen();
2998
+ this.get();
2999
+
3000
+ // we need to make sure the request succeeds since we have no indication
3001
+ // whether the request opened or not until it succeeded.
3002
+ this.setCloseTimeout();
3003
+
3004
+ return this;
3005
+ };
3006
+
3007
+ /**
3008
+ * Check if we need to send data to the Socket.IO server, if we have data in our
3009
+ * buffer we encode it and forward it to the `post` method.
3010
+ *
3011
+ * @api private
3012
+ */
3013
+
3014
+ XHR.prototype.payload = function (payload) {
3015
+ var msgs = [];
3016
+
3017
+ for (var i = 0, l = payload.length; i < l; i++) {
3018
+ msgs.push(io.parser.encodePacket(payload[i]));
3019
+ }
3020
+
3021
+ this.send(io.parser.encodePayload(msgs));
3022
+ };
3023
+
3024
+ /**
3025
+ * Send data to the Socket.IO server.
3026
+ *
3027
+ * @param data The message
3028
+ * @returns {Transport}
3029
+ * @api public
3030
+ */
3031
+
3032
+ XHR.prototype.send = function (data) {
3033
+ this.post(data);
3034
+ return this;
3035
+ };
3036
+
3037
+ /**
3038
+ * Posts a encoded message to the Socket.IO server.
3039
+ *
3040
+ * @param {String} data A encoded message.
3041
+ * @api private
3042
+ */
3043
+
3044
+ function empty () { };
3045
+
3046
+ XHR.prototype.post = function (data) {
3047
+ var self = this;
3048
+ this.socket.setBuffer(true);
3049
+
3050
+ function stateChange () {
3051
+ if (this.readyState == 4) {
3052
+ this.onreadystatechange = empty;
3053
+ self.posting = false;
3054
+
3055
+ if (this.status == 200){
3056
+ self.socket.setBuffer(false);
3057
+ } else {
3058
+ self.onClose();
3059
+ }
3060
+ }
3061
+ }
3062
+
3063
+ function onload () {
3064
+ this.onload = empty;
3065
+ self.socket.setBuffer(false);
3066
+ };
3067
+
3068
+ this.sendXHR = this.request('POST');
3069
+
3070
+ if (global.XDomainRequest && this.sendXHR instanceof XDomainRequest) {
3071
+ this.sendXHR.onload = this.sendXHR.onerror = onload;
3072
+ } else {
3073
+ this.sendXHR.onreadystatechange = stateChange;
3074
+ }
3075
+
3076
+ this.sendXHR.send(data);
3077
+ };
3078
+
3079
+ /**
3080
+ * Disconnects the established `XHR` connection.
3081
+ *
3082
+ * @returns {Transport}
3083
+ * @api public
3084
+ */
3085
+
3086
+ XHR.prototype.close = function () {
3087
+ this.onClose();
3088
+ return this;
3089
+ };
3090
+
3091
+ /**
3092
+ * Generates a configured XHR request
3093
+ *
3094
+ * @param {String} url The url that needs to be requested.
3095
+ * @param {String} method The method the request should use.
3096
+ * @returns {XMLHttpRequest}
3097
+ * @api private
3098
+ */
3099
+
3100
+ XHR.prototype.request = function (method) {
3101
+ var req = io.util.request(this.socket.isXDomain())
3102
+ , query = io.util.query(this.socket.options.query, 't=' + +new Date);
3103
+
3104
+ req.open(method || 'GET', this.prepareUrl() + query, true);
3105
+
3106
+ if (method == 'POST') {
3107
+ try {
3108
+ if (req.setRequestHeader) {
3109
+ req.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
3110
+ } else {
3111
+ // XDomainRequest
3112
+ req.contentType = 'text/plain';
3113
+ }
3114
+ } catch (e) {}
3115
+ }
3116
+
3117
+ return req;
3118
+ };
3119
+
3120
+ /**
3121
+ * Returns the scheme to use for the transport URLs.
3122
+ *
3123
+ * @api private
3124
+ */
3125
+
3126
+ XHR.prototype.scheme = function () {
3127
+ return this.socket.options.secure ? 'https' : 'http';
3128
+ };
3129
+
3130
+ /**
3131
+ * Check if the XHR transports are supported
3132
+ *
3133
+ * @param {Boolean} xdomain Check if we support cross domain requests.
3134
+ * @returns {Boolean}
3135
+ * @api public
3136
+ */
3137
+
3138
+ XHR.check = function (socket, xdomain) {
3139
+ try {
3140
+ if (io.util.request(xdomain)) {
3141
+ return true;
3142
+ }
3143
+ } catch(e) {}
3144
+
3145
+ return false;
3146
+ };
3147
+
3148
+ /**
3149
+ * Check if the XHR transport supports corss domain requests.
3150
+ *
3151
+ * @returns {Boolean}
3152
+ * @api public
3153
+ */
3154
+
3155
+ XHR.xdomainCheck = function () {
3156
+ return XHR.check(null, true);
3157
+ };
3158
+
3159
+ })(
3160
+ 'undefined' != typeof io ? io.Transport : module.exports
3161
+ , 'undefined' != typeof io ? io : module.parent.exports
3162
+ , this
3163
+ );
3164
+
3165
+ /**
3166
+ * socket.io
3167
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
3168
+ * MIT Licensed
3169
+ */
3170
+
3171
+ (function (exports, io) {
3172
+
3173
+ /**
3174
+ * Expose constructor.
3175
+ */
3176
+
3177
+ exports.htmlfile = HTMLFile;
3178
+
3179
+ /**
3180
+ * The HTMLFile transport creates a `forever iframe` based transport
3181
+ * for Internet Explorer. Regular forever iframe implementations will
3182
+ * continuously trigger the browsers buzy indicators. If the forever iframe
3183
+ * is created inside a `htmlfile` these indicators will not be trigged.
3184
+ *
3185
+ * @constructor
3186
+ * @extends {io.Transport.XHR}
3187
+ * @api public
3188
+ */
3189
+
3190
+ function HTMLFile (socket) {
3191
+ io.Transport.XHR.apply(this, arguments);
3192
+ };
3193
+
3194
+ /**
3195
+ * Inherits from XHR transport.
3196
+ */
3197
+
3198
+ io.util.inherit(HTMLFile, io.Transport.XHR);
3199
+
3200
+ /**
3201
+ * Transport name
3202
+ *
3203
+ * @api public
3204
+ */
3205
+
3206
+ HTMLFile.prototype.name = 'htmlfile';
3207
+
3208
+ /**
3209
+ * Creates a new ActiveX `htmlfile` with a forever loading iframe
3210
+ * that can be used to listen to messages. Inside the generated
3211
+ * `htmlfile` a reference will be made to the HTMLFile transport.
3212
+ *
3213
+ * @api private
3214
+ */
3215
+
3216
+ HTMLFile.prototype.get = function () {
3217
+ this.doc = new ActiveXObject('htmlfile');
3218
+ this.doc.open();
3219
+ this.doc.write('<html></html>');
3220
+ this.doc.close();
3221
+ this.doc.parentWindow.s = this;
3222
+
3223
+ var iframeC = this.doc.createElement('div');
3224
+ iframeC.className = 'socketio';
3225
+
3226
+ this.doc.body.appendChild(iframeC);
3227
+ this.iframe = this.doc.createElement('iframe');
3228
+
3229
+ iframeC.appendChild(this.iframe);
3230
+
3231
+ var self = this
3232
+ , query = io.util.query(this.socket.options.query, 't='+ +new Date);
3233
+
3234
+ this.iframe.src = this.prepareUrl() + query;
3235
+
3236
+ io.util.on(window, 'unload', function () {
3237
+ self.destroy();
3238
+ });
3239
+ };
3240
+
3241
+ /**
3242
+ * The Socket.IO server will write script tags inside the forever
3243
+ * iframe, this function will be used as callback for the incoming
3244
+ * information.
3245
+ *
3246
+ * @param {String} data The message
3247
+ * @param {document} doc Reference to the context
3248
+ * @api private
3249
+ */
3250
+
3251
+ HTMLFile.prototype._ = function (data, doc) {
3252
+ this.onData(data);
3253
+ try {
3254
+ var script = doc.getElementsByTagName('script')[0];
3255
+ script.parentNode.removeChild(script);
3256
+ } catch (e) { }
3257
+ };
3258
+
3259
+ /**
3260
+ * Destroy the established connection, iframe and `htmlfile`.
3261
+ * And calls the `CollectGarbage` function of Internet Explorer
3262
+ * to release the memory.
3263
+ *
3264
+ * @api private
3265
+ */
3266
+
3267
+ HTMLFile.prototype.destroy = function () {
3268
+ if (this.iframe){
3269
+ try {
3270
+ this.iframe.src = 'about:blank';
3271
+ } catch(e){}
3272
+
3273
+ this.doc = null;
3274
+ this.iframe.parentNode.removeChild(this.iframe);
3275
+ this.iframe = null;
3276
+
3277
+ CollectGarbage();
3278
+ }
3279
+ };
3280
+
3281
+ /**
3282
+ * Disconnects the established connection.
3283
+ *
3284
+ * @returns {Transport} Chaining.
3285
+ * @api public
3286
+ */
3287
+
3288
+ HTMLFile.prototype.close = function () {
3289
+ this.destroy();
3290
+ return io.Transport.XHR.prototype.close.call(this);
3291
+ };
3292
+
3293
+ /**
3294
+ * Checks if the browser supports this transport. The browser
3295
+ * must have an `ActiveXObject` implementation.
3296
+ *
3297
+ * @return {Boolean}
3298
+ * @api public
3299
+ */
3300
+
3301
+ HTMLFile.check = function () {
3302
+ if ('ActiveXObject' in window){
3303
+ try {
3304
+ var a = new ActiveXObject('htmlfile');
3305
+ return a && io.Transport.XHR.check();
3306
+ } catch(e){}
3307
+ }
3308
+ return false;
3309
+ };
3310
+
3311
+ /**
3312
+ * Check if cross domain requests are supported.
3313
+ *
3314
+ * @returns {Boolean}
3315
+ * @api public
3316
+ */
3317
+
3318
+ HTMLFile.xdomainCheck = function () {
3319
+ // we can probably do handling for sub-domains, we should
3320
+ // test that it's cross domain but a subdomain here
3321
+ return false;
3322
+ };
3323
+
3324
+ /**
3325
+ * Add the transport to your public io.transports array.
3326
+ *
3327
+ * @api private
3328
+ */
3329
+
3330
+ io.transports.push('htmlfile');
3331
+
3332
+ })(
3333
+ 'undefined' != typeof io ? io.Transport : module.exports
3334
+ , 'undefined' != typeof io ? io : module.parent.exports
3335
+ );
3336
+
3337
+ /**
3338
+ * socket.io
3339
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
3340
+ * MIT Licensed
3341
+ */
3342
+
3343
+ (function (exports, io, global) {
3344
+
3345
+ /**
3346
+ * Expose constructor.
3347
+ */
3348
+
3349
+ exports['xhr-polling'] = XHRPolling;
3350
+
3351
+ /**
3352
+ * The XHR-polling transport uses long polling XHR requests to create a
3353
+ * "persistent" connection with the server.
3354
+ *
3355
+ * @constructor
3356
+ * @api public
3357
+ */
3358
+
3359
+ function XHRPolling () {
3360
+ io.Transport.XHR.apply(this, arguments);
3361
+ };
3362
+
3363
+ /**
3364
+ * Inherits from XHR transport.
3365
+ */
3366
+
3367
+ io.util.inherit(XHRPolling, io.Transport.XHR);
3368
+
3369
+ /**
3370
+ * Merge the properties from XHR transport
3371
+ */
3372
+
3373
+ io.util.merge(XHRPolling, io.Transport.XHR);
3374
+
3375
+ /**
3376
+ * Transport name
3377
+ *
3378
+ * @api public
3379
+ */
3380
+
3381
+ XHRPolling.prototype.name = 'xhr-polling';
3382
+
3383
+ /**
3384
+ * Establish a connection, for iPhone and Android this will be done once the page
3385
+ * is loaded.
3386
+ *
3387
+ * @returns {Transport} Chaining.
3388
+ * @api public
3389
+ */
3390
+
3391
+ XHRPolling.prototype.open = function () {
3392
+ var self = this;
3393
+
3394
+ io.Transport.XHR.prototype.open.call(self);
3395
+ return false;
3396
+ };
3397
+
3398
+ /**
3399
+ * Starts a XHR request to wait for incoming messages.
3400
+ *
3401
+ * @api private
3402
+ */
3403
+
3404
+ function empty () {};
3405
+
3406
+ XHRPolling.prototype.get = function () {
3407
+ if (!this.open) return;
3408
+
3409
+ var self = this;
3410
+
3411
+ function stateChange () {
3412
+ if (this.readyState == 4) {
3413
+ this.onreadystatechange = empty;
3414
+
3415
+ if (this.status == 200) {
3416
+ self.onData(this.responseText);
3417
+ self.get();
3418
+ } else {
3419
+ self.onClose();
3420
+ }
3421
+ }
3422
+ };
3423
+
3424
+ function onload () {
3425
+ this.onload = empty;
3426
+ self.onData(this.responseText);
3427
+ self.get();
3428
+ };
3429
+
3430
+ this.xhr = this.request();
3431
+
3432
+ if (global.XDomainRequest && this.xhr instanceof XDomainRequest) {
3433
+ this.xhr.onload = this.xhr.onerror = onload;
3434
+ } else {
3435
+ this.xhr.onreadystatechange = stateChange;
3436
+ }
3437
+
3438
+ this.xhr.send(null);
3439
+ };
3440
+
3441
+ /**
3442
+ * Handle the unclean close behavior.
3443
+ *
3444
+ * @api private
3445
+ */
3446
+
3447
+ XHRPolling.prototype.onClose = function () {
3448
+ io.Transport.XHR.prototype.onClose.call(this);
3449
+
3450
+ if (this.xhr) {
3451
+ this.xhr.onreadystatechange = this.xhr.onload = empty;
3452
+ try {
3453
+ this.xhr.abort();
3454
+ } catch(e){}
3455
+ this.xhr = null;
3456
+ }
3457
+ };
3458
+
3459
+ /**
3460
+ * Webkit based browsers show a infinit spinner when you start a XHR request
3461
+ * before the browsers onload event is called so we need to defer opening of
3462
+ * the transport until the onload event is called. Wrapping the cb in our
3463
+ * defer method solve this.
3464
+ *
3465
+ * @param {Socket} socket The socket instance that needs a transport
3466
+ * @param {Function} fn The callback
3467
+ * @api private
3468
+ */
3469
+
3470
+ XHRPolling.prototype.ready = function (socket, fn) {
3471
+ var self = this;
3472
+
3473
+ io.util.defer(function () {
3474
+ fn.call(self);
3475
+ });
3476
+ };
3477
+
3478
+ /**
3479
+ * Add the transport to your public io.transports array.
3480
+ *
3481
+ * @api private
3482
+ */
3483
+
3484
+ io.transports.push('xhr-polling');
3485
+
3486
+ })(
3487
+ 'undefined' != typeof io ? io.Transport : module.exports
3488
+ , 'undefined' != typeof io ? io : module.parent.exports
3489
+ , this
3490
+ );
3491
+
3492
+ /**
3493
+ * socket.io
3494
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
3495
+ * MIT Licensed
3496
+ */
3497
+
3498
+ (function (exports, io) {
3499
+
3500
+ /**
3501
+ * Expose constructor.
3502
+ */
3503
+
3504
+ exports['jsonp-polling'] = JSONPPolling;
3505
+
3506
+ /**
3507
+ * The JSONP transport creates an persistent connection by dynamically
3508
+ * inserting a script tag in the page. This script tag will receive the
3509
+ * information of the Socket.IO server. When new information is received
3510
+ * it creates a new script tag for the new data stream.
3511
+ *
3512
+ * @constructor
3513
+ * @extends {io.Transport.xhr-polling}
3514
+ * @api public
3515
+ */
3516
+
3517
+ function JSONPPolling (socket) {
3518
+ io.Transport['xhr-polling'].apply(this, arguments);
3519
+
3520
+ this.index = io.j.length;
3521
+
3522
+ var self = this;
3523
+
3524
+ io.j.push(function (msg) {
3525
+ self._(msg);
3526
+ });
3527
+ };
3528
+
3529
+ /**
3530
+ * Inherits from XHR polling transport.
3531
+ */
3532
+
3533
+ io.util.inherit(JSONPPolling, io.Transport['xhr-polling']);
3534
+
3535
+ /**
3536
+ * Transport name
3537
+ *
3538
+ * @api public
3539
+ */
3540
+
3541
+ JSONPPolling.prototype.name = 'jsonp-polling';
3542
+
3543
+ /**
3544
+ * Posts a encoded message to the Socket.IO server using an iframe.
3545
+ * The iframe is used because script tags can create POST based requests.
3546
+ * The iframe is positioned outside of the view so the user does not
3547
+ * notice it's existence.
3548
+ *
3549
+ * @param {String} data A encoded message.
3550
+ * @api private
3551
+ */
3552
+
3553
+ JSONPPolling.prototype.post = function (data) {
3554
+ var self = this
3555
+ , query = io.util.query(
3556
+ this.socket.options.query
3557
+ , 't='+ (+new Date) + '&i=' + this.index
3558
+ );
3559
+
3560
+ if (!this.form) {
3561
+ var form = document.createElement('form')
3562
+ , area = document.createElement('textarea')
3563
+ , id = this.iframeId = 'socketio_iframe_' + this.index
3564
+ , iframe;
3565
+
3566
+ form.className = 'socketio';
3567
+ form.style.position = 'absolute';
3568
+ form.style.top = '-1000px';
3569
+ form.style.left = '-1000px';
3570
+ form.target = id;
3571
+ form.method = 'POST';
3572
+ form.setAttribute('accept-charset', 'utf-8');
3573
+ area.name = 'd';
3574
+ form.appendChild(area);
3575
+ document.body.appendChild(form);
3576
+
3577
+ this.form = form;
3578
+ this.area = area;
3579
+ }
3580
+
3581
+ this.form.action = this.prepareUrl() + query;
3582
+
3583
+ function complete () {
3584
+ initIframe();
3585
+ self.socket.setBuffer(false);
3586
+ };
3587
+
3588
+ function initIframe () {
3589
+ if (self.iframe) {
3590
+ self.form.removeChild(self.iframe);
3591
+ }
3592
+
3593
+ try {
3594
+ // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
3595
+ iframe = document.createElement('<iframe name="'+ self.iframeId +'">');
3596
+ } catch (e) {
3597
+ iframe = document.createElement('iframe');
3598
+ iframe.name = self.iframeId;
3599
+ }
3600
+
3601
+ iframe.id = self.iframeId;
3602
+
3603
+ self.form.appendChild(iframe);
3604
+ self.iframe = iframe;
3605
+ };
3606
+
3607
+ initIframe();
3608
+
3609
+ this.area.value = data;
3610
+
3611
+ try {
3612
+ this.form.submit();
3613
+ } catch(e) {}
3614
+
3615
+ if (this.iframe.attachEvent) {
3616
+ iframe.onreadystatechange = function () {
3617
+ if (self.iframe.readyState == 'complete') {
3618
+ complete();
3619
+ }
3620
+ };
3621
+ } else {
3622
+ this.iframe.onload = complete;
3623
+ }
3624
+
3625
+ this.socket.setBuffer(true);
3626
+ };
3627
+
3628
+ /**
3629
+ * Creates a new JSONP poll that can be used to listen
3630
+ * for messages from the Socket.IO server.
3631
+ *
3632
+ * @api private
3633
+ */
3634
+
3635
+ JSONPPolling.prototype.get = function () {
3636
+ var self = this
3637
+ , script = document.createElement('script')
3638
+ , query = io.util.query(
3639
+ this.socket.options.query
3640
+ , 't='+ (+new Date) + '&i=' + this.index
3641
+ );
3642
+
3643
+ if (this.script) {
3644
+ this.script.parentNode.removeChild(this.script);
3645
+ this.script = null;
3646
+ }
3647
+
3648
+ script.async = true;
3649
+ script.src = this.prepareUrl() + query;
3650
+ script.onerror = function () {
3651
+ self.onClose();
3652
+ };
3653
+
3654
+ var insertAt = document.getElementsByTagName('script')[0]
3655
+ insertAt.parentNode.insertBefore(script, insertAt);
3656
+ this.script = script;
3657
+ };
3658
+
3659
+ /**
3660
+ * Callback function for the incoming message stream from the Socket.IO server.
3661
+ *
3662
+ * @param {String} data The message
3663
+ * @api private
3664
+ */
3665
+
3666
+ JSONPPolling.prototype._ = function (msg) {
3667
+ this.onData(msg);
3668
+ if (this.open) {
3669
+ this.get();
3670
+ }
3671
+ return this;
3672
+ };
3673
+
3674
+ /**
3675
+ * Checks if browser supports this transport.
3676
+ *
3677
+ * @return {Boolean}
3678
+ * @api public
3679
+ */
3680
+
3681
+ JSONPPolling.check = function () {
3682
+ return true;
3683
+ };
3684
+
3685
+ /**
3686
+ * Check if cross domain requests are supported
3687
+ *
3688
+ * @returns {Boolean}
3689
+ * @api public
3690
+ */
3691
+
3692
+ JSONPPolling.xdomainCheck = function () {
3693
+ return true;
3694
+ };
3695
+
3696
+ /**
3697
+ * Add the transport to your public io.transports array.
3698
+ *
3699
+ * @api private
3700
+ */
3701
+
3702
+ io.transports.push('jsonp-polling');
3703
+
3704
+ })(
3705
+ 'undefined' != typeof io ? io.Transport : module.exports
3706
+ , 'undefined' != typeof io ? io : module.parent.exports
3707
+ );