webmate 0.1.0

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