PageRenderTime 443ms CodeModel.GetById 101ms app.highlight 253ms RepoModel.GetById 48ms app.codeStats 1ms

/js/lib/Socket.IO-node/support/socket.io-client/socket.io.js

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
JavaScript | 1429 lines | 1018 code | 194 blank | 217 comment | 236 complexity | b1652545bcbdf7fea93ee1b1a308028b MD5 | raw file
   1/** Socket.IO 0.6.2 - Built with build.js */
   2/**
   3 * Socket.IO client
   4 * 
   5 * @author Guillermo Rauch <guillermo@learnboost.com>
   6 * @license The MIT license.
   7 * @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
   8 */
   9
  10var io = this.io = {
  11	version: '0.6.2',
  12	
  13	setPath: function(path){
  14		if (window.console && console.error) console.error('io.setPath will be removed. Please set the variable WEB_SOCKET_SWF_LOCATION pointing to WebSocketMain.swf');
  15		this.path = /\/$/.test(path) ? path : path + '/';
  16    WEB_SOCKET_SWF_LOCATION = path + 'lib/vendor/web-socket-js/WebSocketMain.swf';
  17	}
  18};
  19
  20if ('jQuery' in this) jQuery.io = this.io;
  21
  22if (typeof window != 'undefined'){
  23  // WEB_SOCKET_SWF_LOCATION = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//cdn.socket.io/' + this.io.version + '/WebSocketMain.swf';
  24  if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined')
  25    WEB_SOCKET_SWF_LOCATION = '/socket.io/lib/vendor/web-socket-js/WebSocketMain.swf';
  26}
  27
  28/**
  29 * Socket.IO client
  30 * 
  31 * @author Guillermo Rauch <guillermo@learnboost.com>
  32 * @license The MIT license.
  33 * @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
  34 */
  35
  36(function(){
  37	var io = this.io;
  38
  39	var _pageLoaded = false;
  40
  41	io.util = {
  42
  43		ios: false,
  44
  45		load: function(fn){
  46			if (/loaded|complete/.test(document.readyState) || _pageLoaded) return fn();
  47			if ('attachEvent' in window){
  48				window.attachEvent('onload', fn);
  49			} else {
  50				window.addEventListener('load', fn, false);
  51			}
  52		},
  53
  54		inherit: function(ctor, superCtor){
  55			// no support for `instanceof` for now
  56			for (var i in superCtor.prototype){
  57				ctor.prototype[i] = superCtor.prototype[i];
  58			}
  59		},
  60
  61		indexOf: function(arr, item, from){
  62			for (var l = arr.length, i = (from < 0) ? Math.max(0, l + from) : from || 0; i < l; i++){
  63				if (arr[i] === item) return i;
  64			}
  65			return -1;
  66		},
  67
  68		isArray: function(obj){
  69			return Object.prototype.toString.call(obj) === '[object Array]';
  70		},
  71		
  72    merge: function(target, additional){
  73      for (var i in additional)
  74        if (additional.hasOwnProperty(i))
  75          target[i] = additional[i];
  76    }
  77
  78	};
  79
  80	io.util.ios = /iphone|ipad/i.test(navigator.userAgent);
  81	io.util.android = /android/i.test(navigator.userAgent);
  82	io.util.opera = /opera/i.test(navigator.userAgent);
  83
  84	io.util.load(function(){
  85		_pageLoaded = true;
  86	});
  87
  88})();
  89
  90/**
  91 * Socket.IO client
  92 * 
  93 * @author Guillermo Rauch <guillermo@learnboost.com>
  94 * @license The MIT license.
  95 * @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
  96 */
  97
  98// abstract
  99
 100(function(){
 101	var io = this.io;
 102	
 103	var frame = '~m~',
 104	
 105	stringify = function(message){
 106		if (Object.prototype.toString.call(message) == '[object Object]'){
 107			if (!('JSON' in window)){
 108				if ('console' in window && console.error) console.error('Trying to encode as JSON, but JSON.stringify is missing.');
 109				return '{ "$error": "Invalid message" }';
 110			}
 111			return '~j~' + JSON.stringify(message);
 112		} else {
 113			return String(message);
 114		}
 115	};
 116	
 117	Transport = io.Transport = function(base, options){
 118		this.base = base;
 119		this.options = {
 120			timeout: 15000 // based on heartbeat interval default
 121		};
 122		io.util.merge(this.options, options);
 123	};
 124
 125	Transport.prototype.send = function(){
 126		throw new Error('Missing send() implementation');
 127	};
 128
 129	Transport.prototype.connect = function(){
 130		throw new Error('Missing connect() implementation');
 131	};
 132
 133	Transport.prototype.disconnect = function(){
 134		throw new Error('Missing disconnect() implementation');
 135	};
 136	
 137	Transport.prototype._encode = function(messages){
 138		var ret = '', message,
 139				messages = io.util.isArray(messages) ? messages : [messages];
 140		for (var i = 0, l = messages.length; i < l; i++){
 141			message = messages[i] === null || messages[i] === undefined ? '' : stringify(messages[i]);
 142			ret += frame + message.length + frame + message;
 143		}
 144		return ret;
 145	};
 146	
 147	Transport.prototype._decode = function(data){
 148		var messages = [], number, n;
 149		do {
 150			if (data.substr(0, 3) !== frame) return messages;
 151			data = data.substr(3);
 152			number = '', n = '';
 153			for (var i = 0, l = data.length; i < l; i++){
 154				n = Number(data.substr(i, 1));
 155				if (data.substr(i, 1) == n){
 156					number += n;
 157				} else {	
 158					data = data.substr(number.length + frame.length);
 159					number = Number(number);
 160					break;
 161				} 
 162			}
 163			messages.push(data.substr(0, number)); // here
 164			data = data.substr(number);
 165		} while(data !== '');
 166		return messages;
 167	};
 168	
 169	Transport.prototype._onData = function(data){
 170		this._setTimeout();
 171		var msgs = this._decode(data);
 172		if (msgs && msgs.length){
 173			for (var i = 0, l = msgs.length; i < l; i++){
 174				this._onMessage(msgs[i]);
 175			}
 176		}
 177	};
 178	
 179	Transport.prototype._setTimeout = function(){
 180		var self = this;
 181		if (this._timeout) clearTimeout(this._timeout);
 182		this._timeout = setTimeout(function(){
 183			self._onTimeout();
 184		}, this.options.timeout);
 185	};
 186	
 187	Transport.prototype._onTimeout = function(){
 188		this._onDisconnect();
 189	};
 190	
 191	Transport.prototype._onMessage = function(message){
 192		if (!this.sessionid){
 193			this.sessionid = message;
 194			this._onConnect();
 195		} else if (message.substr(0, 3) == '~h~'){
 196			this._onHeartbeat(message.substr(3));
 197		} else if (message.substr(0, 3) == '~j~'){
 198			this.base._onMessage(JSON.parse(message.substr(3)));
 199		} else {
 200			this.base._onMessage(message);
 201		}
 202	},
 203	
 204	Transport.prototype._onHeartbeat = function(heartbeat){
 205		this.send('~h~' + heartbeat); // echo
 206	};
 207	
 208	Transport.prototype._onConnect = function(){
 209		this.connected = true;
 210		this.connecting = false;
 211		this.base._onConnect();
 212		this._setTimeout();
 213	};
 214
 215	Transport.prototype._onDisconnect = function(){
 216		this.connecting = false;
 217		this.connected = false;
 218		this.sessionid = null;
 219		this.base._onDisconnect();
 220	};
 221
 222	Transport.prototype._prepareUrl = function(){
 223		return (this.base.options.secure ? 'https' : 'http') 
 224			+ '://' + this.base.host 
 225			+ ':' + this.base.options.port
 226			+ '/' + this.base.options.resource
 227			+ '/' + this.type
 228			+ (this.sessionid ? ('/' + this.sessionid) : '/');
 229	};
 230
 231})();
 232/**
 233 * Socket.IO client
 234 * 
 235 * @author Guillermo Rauch <guillermo@learnboost.com>
 236 * @license The MIT license.
 237 * @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
 238 */
 239
 240(function(){
 241	var io = this.io;
 242	
 243	var empty = new Function,
 244	    
 245	XMLHttpRequestCORS = (function(){
 246		if (!('XMLHttpRequest' in window)) return false;
 247		// CORS feature detection
 248		var a = new XMLHttpRequest();
 249		return a.withCredentials != undefined;
 250	})(),
 251	
 252	request = function(xdomain){
 253		if ('XDomainRequest' in window && xdomain) return new XDomainRequest();
 254		if ('XMLHttpRequest' in window && (!xdomain || XMLHttpRequestCORS)) return new XMLHttpRequest();
 255		if (!xdomain){
 256			try {
 257				var a = new ActiveXObject('MSXML2.XMLHTTP');
 258				return a;
 259			} catch(e){}
 260		
 261			try {
 262				var b = new ActiveXObject('Microsoft.XMLHTTP');
 263				return b;
 264			} catch(e){}
 265		}
 266		return false;
 267	},
 268	
 269	XHR = io.Transport.XHR = function(){
 270		io.Transport.apply(this, arguments);
 271		this._sendBuffer = [];
 272	};
 273	
 274	io.util.inherit(XHR, io.Transport);
 275	
 276	XHR.prototype.connect = function(){
 277		this._get();
 278		return this;
 279	};
 280	
 281	XHR.prototype._checkSend = function(){
 282		if (!this._posting && this._sendBuffer.length){
 283			var encoded = this._encode(this._sendBuffer);
 284			this._sendBuffer = [];
 285			this._send(encoded);
 286		}
 287	};
 288	
 289	XHR.prototype.send = function(data){
 290		if (io.util.isArray(data)){
 291			this._sendBuffer.push.apply(this._sendBuffer, data);
 292		} else {
 293			this._sendBuffer.push(data);
 294		}
 295		this._checkSend();
 296		return this;
 297	};
 298	
 299	XHR.prototype._send = function(data){
 300		var self = this;
 301		this._posting = true;
 302		this._sendXhr = this._request('send', 'POST');
 303		this._sendXhr.onreadystatechange = function(){
 304			var status;
 305			if (self._sendXhr.readyState == 4){
 306				self._sendXhr.onreadystatechange = empty;
 307				try { status = self._sendXhr.status; } catch(e){}
 308				self._posting = false;
 309				if (status == 200){
 310					self._checkSend();
 311				} else {
 312					self._onDisconnect();
 313				}
 314			}
 315		};
 316		this._sendXhr.send('data=' + encodeURIComponent(data));
 317	};
 318	
 319	XHR.prototype.disconnect = function(){
 320		// send disconnection signal
 321		this._onDisconnect();
 322		return this;
 323	};
 324	
 325	XHR.prototype._onDisconnect = function(){
 326		if (this._xhr){
 327			this._xhr.onreadystatechange = empty;
 328      try {
 329        this._xhr.abort();
 330      } catch(e){}
 331			this._xhr = null;
 332		}
 333		if (this._sendXhr){
 334      this._sendXhr.onreadystatechange = empty;
 335      try {
 336        this._sendXhr.abort();
 337      } catch(e){}
 338			this._sendXhr = null;
 339		}
 340		this._sendBuffer = [];
 341		io.Transport.prototype._onDisconnect.call(this);
 342	};
 343	
 344	XHR.prototype._request = function(url, method, multipart){
 345		var req = request(this.base._isXDomain());
 346		if (multipart) req.multipart = true;
 347		req.open(method || 'GET', this._prepareUrl() + (url ? '/' + url : ''));
 348		if (method == 'POST' && 'setRequestHeader' in req){
 349			req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8');
 350		}
 351		return req;
 352	};
 353	
 354	XHR.check = function(xdomain){
 355		try {
 356			if (request(xdomain)) return true;
 357		} catch(e){}
 358		return false;
 359	};
 360	
 361	XHR.xdomainCheck = function(){
 362		return XHR.check(true);
 363	};
 364	
 365	XHR.request = request;
 366	
 367})();
 368
 369/**
 370 * Socket.IO client
 371 * 
 372 * @author Guillermo Rauch <guillermo@learnboost.com>
 373 * @license The MIT license.
 374 * @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
 375 */
 376
 377(function(){
 378	var io = this.io;
 379	
 380	var WS = io.Transport.websocket = function(){
 381		io.Transport.apply(this, arguments);
 382	};
 383	
 384	io.util.inherit(WS, io.Transport);
 385	
 386	WS.prototype.type = 'websocket';
 387	
 388	WS.prototype.connect = function(){
 389		var self = this;
 390		this.socket = new WebSocket(this._prepareUrl());
 391		this.socket.onmessage = function(ev){ self._onData(ev.data); };
 392		this.socket.onclose = function(ev){ self._onClose(); };
 393    this.socket.onerror = function(e){ self._onError(e); };
 394		return this;
 395	};
 396	
 397	WS.prototype.send = function(data){
 398		if (this.socket) this.socket.send(this._encode(data));
 399		return this;
 400	};
 401	
 402	WS.prototype.disconnect = function(){
 403		if (this.socket) this.socket.close();
 404		return this;
 405	};
 406	
 407	WS.prototype._onClose = function(){
 408		this._onDisconnect();
 409		return this;
 410	};
 411
 412  WS.prototype._onError = function(e){
 413    this.base.emit('error', [e]);
 414  };
 415	
 416	WS.prototype._prepareUrl = function(){
 417		return (this.base.options.secure ? 'wss' : 'ws') 
 418		+ '://' + this.base.host 
 419		+ ':' + this.base.options.port
 420		+ '/' + this.base.options.resource
 421		+ '/' + this.type
 422		+ (this.sessionid ? ('/' + this.sessionid) : '');
 423	};
 424	
 425	WS.check = function(){
 426		// we make sure WebSocket is not confounded with a previously loaded flash WebSocket
 427		return 'WebSocket' in window && WebSocket.prototype && ( WebSocket.prototype.send && !!WebSocket.prototype.send.toString().match(/native/i)) && typeof WebSocket !== "undefined";
 428	};
 429
 430	WS.xdomainCheck = function(){
 431		return true;
 432	};
 433	
 434})();
 435
 436/**
 437 * Socket.IO client
 438 * 
 439 * @author Guillermo Rauch <guillermo@learnboost.com>
 440 * @license The MIT license.
 441 * @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
 442 */
 443
 444(function(){
 445	var io = this.io;
 446	
 447	var Flashsocket = io.Transport.flashsocket = function(){
 448		io.Transport.websocket.apply(this, arguments);
 449	};
 450	
 451	io.util.inherit(Flashsocket, io.Transport.websocket);
 452	
 453	Flashsocket.prototype.type = 'flashsocket';
 454	
 455	Flashsocket.prototype.connect = function(){
 456		var self = this, args = arguments;
 457		WebSocket.__addTask(function(){
 458			io.Transport.websocket.prototype.connect.apply(self, args);
 459		});
 460		return this;
 461	};
 462	
 463	Flashsocket.prototype.send = function(){
 464		var self = this, args = arguments;
 465		WebSocket.__addTask(function(){
 466			io.Transport.websocket.prototype.send.apply(self, args);
 467		});
 468		return this;
 469	};
 470	
 471	Flashsocket.check = function(){
 472		if (typeof WebSocket == 'undefined' || !('__addTask' in WebSocket)) return false;
 473		if (io.util.opera) return false; // opera is buggy with this transport
 474		if ('navigator' in window && 'plugins' in navigator && navigator.plugins['Shockwave Flash']){
 475			return !!navigator.plugins['Shockwave Flash'].description;
 476	  }
 477		if ('ActiveXObject' in window) {
 478			try {
 479				return !!new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
 480			} catch (e) {}
 481		}
 482		return false;
 483	};
 484	
 485	Flashsocket.xdomainCheck = function(){
 486		return true;
 487	};
 488	
 489})();
 490/**
 491 * Socket.IO client
 492 * 
 493 * @author Guillermo Rauch <guillermo@learnboost.com>
 494 * @license The MIT license.
 495 * @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
 496 */
 497
 498(function(){
 499	var io = this.io;
 500	
 501	var HTMLFile = io.Transport.htmlfile = function(){
 502		io.Transport.XHR.apply(this, arguments);
 503	};
 504	
 505	io.util.inherit(HTMLFile, io.Transport.XHR);
 506	
 507	HTMLFile.prototype.type = 'htmlfile';
 508	
 509	HTMLFile.prototype._get = function(){
 510		var self = this;
 511		this._open();
 512		window.attachEvent('onunload', function(){ self._destroy(); });
 513	};
 514	
 515	HTMLFile.prototype._open = function(){
 516		this._doc = new ActiveXObject('htmlfile');
 517		this._doc.open();
 518		this._doc.write('<html></html>');
 519		this._doc.parentWindow.s = this;
 520		this._doc.close();
 521
 522		var _iframeC = this._doc.createElement('div');
 523		this._doc.body.appendChild(_iframeC);
 524		this._iframe = this._doc.createElement('iframe');
 525		_iframeC.appendChild(this._iframe);
 526		this._iframe.src = this._prepareUrl() + '/' + (+ new Date);
 527	};
 528	
 529	HTMLFile.prototype._ = function(data, doc){
 530		this._onData(data);
 531		var script = doc.getElementsByTagName('script')[0];
 532		script.parentNode.removeChild(script);
 533	};
 534
 535  HTMLFile.prototype._destroy = function(){
 536    if (this._iframe){
 537      this._iframe.src = 'about:blank';
 538      this._doc = null;
 539      CollectGarbage();
 540    }
 541  };
 542	
 543	HTMLFile.prototype.disconnect = function(){
 544		this._destroy();
 545		return io.Transport.XHR.prototype.disconnect.call(this);
 546	};
 547	
 548	HTMLFile.check = function(){
 549		if ('ActiveXObject' in window){
 550			try {
 551				var a = new ActiveXObject('htmlfile');
 552				return a && io.Transport.XHR.check();
 553			} catch(e){}
 554		}
 555		return false;
 556	};
 557
 558	HTMLFile.xdomainCheck = function(){
 559		// we can probably do handling for sub-domains, we should test that it's cross domain but a subdomain here
 560		return false;
 561	};
 562	
 563})();
 564/**
 565 * Socket.IO client
 566 * 
 567 * @author Guillermo Rauch <guillermo@learnboost.com>
 568 * @license The MIT license.
 569 * @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
 570 */
 571
 572(function(){
 573	var io = this.io;
 574	
 575	var XHRMultipart = io.Transport['xhr-multipart'] = function(){
 576		io.Transport.XHR.apply(this, arguments);
 577	};
 578	
 579	io.util.inherit(XHRMultipart, io.Transport.XHR);
 580	
 581	XHRMultipart.prototype.type = 'xhr-multipart';
 582	
 583	XHRMultipart.prototype._get = function(){
 584		var self = this;
 585		this._xhr = this._request('', 'GET', true);
 586		this._xhr.onreadystatechange = function(){
 587			if (self._xhr.readyState == 4) self._onData(self._xhr.responseText);
 588		};
 589		this._xhr.send(null);
 590	};
 591	
 592	XHRMultipart.check = function(){
 593		return 'XMLHttpRequest' in window && 'prototype' in XMLHttpRequest && 'multipart' in XMLHttpRequest.prototype;
 594	};
 595
 596	XHRMultipart.xdomainCheck = function(){
 597		return true;
 598	};
 599	
 600})();
 601
 602/**
 603 * Socket.IO client
 604 * 
 605 * @author Guillermo Rauch <guillermo@learnboost.com>
 606 * @license The MIT license.
 607 * @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
 608 */
 609
 610(function(){
 611	var io = this.io;
 612
 613	var empty = new Function(),
 614
 615	XHRPolling = io.Transport['xhr-polling'] = function(){
 616		io.Transport.XHR.apply(this, arguments);
 617	};
 618
 619	io.util.inherit(XHRPolling, io.Transport.XHR);
 620
 621	XHRPolling.prototype.type = 'xhr-polling';
 622
 623	XHRPolling.prototype.connect = function(){
 624		if (io.util.ios || io.util.android){
 625			var self = this;
 626			io.util.load(function(){
 627				setTimeout(function(){
 628					io.Transport.XHR.prototype.connect.call(self);
 629				}, 10);
 630			});
 631		} else {
 632			io.Transport.XHR.prototype.connect.call(this);
 633		}
 634	};
 635
 636	XHRPolling.prototype._get = function(){
 637		var self = this;
 638		this._xhr = this._request(+ new Date, 'GET');
 639    this._xhr.onreadystatechange = function(){
 640      var status;
 641      if (self._xhr.readyState == 4){
 642        self._xhr.onreadystatechange = empty;
 643        try { status = self._xhr.status; } catch(e){}
 644        if (status == 200){
 645          self._onData(self._xhr.responseText);
 646          self._get();
 647        } else {
 648          self._onDisconnect();
 649        }
 650      }
 651    };
 652		this._xhr.send(null);
 653	};
 654
 655	XHRPolling.check = function(){
 656		return io.Transport.XHR.check();
 657	};
 658
 659	XHRPolling.xdomainCheck = function(){
 660		return io.Transport.XHR.xdomainCheck();
 661	};
 662
 663})();
 664
 665/**
 666 * Socket.IO client
 667 * 
 668 * @author Guillermo Rauch <guillermo@learnboost.com>
 669 * @license The MIT license.
 670 * @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
 671 */
 672
 673(function(){
 674	var io = this.io;
 675	
 676	io.JSONP = [];
 677	
 678	JSONPPolling = io.Transport['jsonp-polling'] = function(){
 679		io.Transport.XHR.apply(this, arguments);
 680		this._insertAt = document.getElementsByTagName('script')[0];
 681		this._index = io.JSONP.length;
 682		io.JSONP.push(this);
 683	};
 684	
 685	io.util.inherit(JSONPPolling, io.Transport['xhr-polling']);
 686	
 687	JSONPPolling.prototype.type = 'jsonp-polling';
 688	
 689	JSONPPolling.prototype._send = function(data){
 690		var self = this;
 691		if (!('_form' in this)){
 692			var form = document.createElement('FORM'),
 693				area = document.createElement('TEXTAREA'),
 694				id = this._iframeId = 'socket_io_iframe_' + this._index,
 695				iframe;
 696	
 697			form.style.position = 'absolute';
 698			form.style.top = '-1000px';
 699			form.style.left = '-1000px';
 700			form.target = id;
 701			form.method = 'POST';
 702			form.action = this._prepareUrl() + '/' + (+new Date) + '/' + this._index;
 703			area.name = 'data';
 704			form.appendChild(area);
 705			this._insertAt.parentNode.insertBefore(form, this._insertAt);
 706			document.body.appendChild(form);
 707	
 708			this._form = form;
 709			this._area = area;
 710		}
 711	
 712		function complete(){
 713			initIframe();
 714			self._posting = false;
 715			self._checkSend();
 716		};
 717	
 718		function initIframe(){
 719			if (self._iframe){
 720				self._form.removeChild(self._iframe);
 721			} 
 722	
 723			try {
 724				// ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
 725				iframe = document.createElement('<iframe name="'+ self._iframeId +'">');
 726			} catch(e){
 727				iframe = document.createElement('iframe');
 728				iframe.name = self._iframeId;
 729			}
 730	
 731			iframe.id = self._iframeId;
 732	
 733			self._form.appendChild(iframe);
 734			self._iframe = iframe;
 735		};
 736	
 737		initIframe();
 738	
 739		this._posting = true;
 740		this._area.value = data;
 741	
 742		try {
 743			this._form.submit();
 744		} catch(e){}
 745	
 746		if (this._iframe.attachEvent){
 747			iframe.onreadystatechange = function(){
 748				if (self._iframe.readyState == 'complete') complete();
 749			};
 750		} else {
 751			this._iframe.onload = complete;
 752		}
 753	};
 754	
 755	JSONPPolling.prototype._get = function(){
 756		var self = this,
 757				script = document.createElement('SCRIPT');
 758		if (this._script){
 759			this._script.parentNode.removeChild(this._script);
 760			this._script = null;
 761		}
 762		script.async = true;
 763		script.src = this._prepareUrl() + '/' + (+new Date) + '/' + this._index;
 764		script.onerror = function(){
 765			self._onDisconnect();
 766		};
 767		this._insertAt.parentNode.insertBefore(script, this._insertAt);
 768		this._script = script;
 769	};
 770	
 771	JSONPPolling.prototype._ = function(){
 772		this._onData.apply(this, arguments);
 773		this._get();
 774		return this;
 775	};
 776	
 777	JSONPPolling.check = function(){
 778		return true;
 779	};
 780	
 781	JSONPPolling.xdomainCheck = function(){
 782		return true;
 783	};
 784})();
 785/**
 786 * Socket.IO client
 787 * 
 788 * @author Guillermo Rauch <guillermo@learnboost.com>
 789 * @license The MIT license.
 790 * @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
 791 */
 792
 793(function(){
 794	var io = this.io;
 795	
 796	var Socket = io.Socket = function(host, options){
 797		this.host = host || document.domain;
 798		this.options = {
 799			secure: false,
 800			document: document,
 801			port: document.location.port || 80,
 802			resource: 'socket.io',
 803			transports: ['websocket', 'flashsocket', 'htmlfile', 'xhr-multipart', 'xhr-polling', 'jsonp-polling'],
 804			transportOptions: {
 805				'xhr-polling': {
 806					timeout: 25000 // based on polling duration default
 807				},
 808				'jsonp-polling': {
 809					timeout: 25000
 810				}
 811			},
 812			connectTimeout: 5000,
 813			reconnect: true,
 814			reconnectionDelay: 500,
 815			maxReconnectionAttempts: 10,
 816			tryTransportsOnConnectTimeout: true,
 817			rememberTransport: true
 818		};
 819		io.util.merge(this.options, options);
 820		this.connected = false;
 821		this.connecting = false;
 822		this._events = {};
 823		this.transport = this.getTransport();
 824		if (!this.transport && 'console' in window) console.error('No transport available');
 825	};
 826	
 827	Socket.prototype.getTransport = function(override){
 828		var transports = override || this.options.transports, match;
 829		if (this.options.rememberTransport && !override){
 830			match = this.options.document.cookie.match('(?:^|;)\\s*socketio=([^;]*)');
 831			if (match){
 832				this._rememberedTransport = true;
 833				transports = [decodeURIComponent(match[1])];
 834			}
 835		} 
 836		for (var i = 0, transport; transport = transports[i]; i++){
 837			if (io.Transport[transport] 
 838				&& io.Transport[transport].check() 
 839				&& (!this._isXDomain() || io.Transport[transport].xdomainCheck())){
 840				return new io.Transport[transport](this, this.options.transportOptions[transport] || {});
 841			}
 842		}
 843		return null;
 844	};
 845	
 846	Socket.prototype.connect = function(){
 847		if (this.transport && !this.connected){
 848			if (this.connecting) this.disconnect(true);
 849			this.connecting = true;
 850			this.emit('connecting', [this.transport.type]);
 851			this.transport.connect();
 852			if (this.options.connectTimeout){
 853				var self = this;
 854				this.connectTimeoutTimer = setTimeout(function(){
 855					if (!self.connected){
 856						self.disconnect(true);
 857						if (self.options.tryTransportsOnConnectTimeout && !self._rememberedTransport){
 858							if(!self._remainingTransports) self._remainingTransports = self.options.transports.slice(0);
 859							var transports = self._remainingTransports;
 860							while(transports.length > 0 && transports.splice(0,1)[0] != self.transport.type){}
 861							if(transports.length){
 862								self.transport = self.getTransport(transports);
 863								self.connect();
 864							}
 865						}
 866						if(!self._remainingTransports || self._remainingTransports.length == 0) self.emit('connect_failed');
 867					}
 868					if(self._remainingTransports && self._remainingTransports.length == 0) delete self._remainingTransports;
 869				}, this.options.connectTimeout);
 870			}
 871		}
 872		return this;
 873	};
 874	
 875	Socket.prototype.send = function(data){
 876		if (!this.transport || !this.transport.connected) return this._queue(data);
 877		this.transport.send(data);
 878		return this;
 879	};
 880	
 881	Socket.prototype.disconnect = function(reconnect){
 882    if (this.connectTimeoutTimer) clearTimeout(this.connectTimeoutTimer);
 883		if (!reconnect) this.options.reconnect = false;
 884		this.transport.disconnect();
 885		return this;
 886	};
 887	
 888	Socket.prototype.on = function(name, fn){
 889		if (!(name in this._events)) this._events[name] = [];
 890		this._events[name].push(fn);
 891		return this;
 892	};
 893	
 894  Socket.prototype.emit = function(name, args){
 895    if (name in this._events){
 896      var events = this._events[name].concat();
 897      for (var i = 0, ii = events.length; i < ii; i++)
 898        events[i].apply(this, args === undefined ? [] : args);
 899    }
 900    return this;
 901  };
 902
 903	Socket.prototype.removeEvent = function(name, fn){
 904		if (name in this._events){
 905			for (var a = 0, l = this._events[name].length; a < l; a++)
 906				if (this._events[name][a] == fn) this._events[name].splice(a, 1);		
 907		}
 908		return this;
 909	};
 910	
 911	Socket.prototype._queue = function(message){
 912		if (!('_queueStack' in this)) this._queueStack = [];
 913		this._queueStack.push(message);
 914		return this;
 915	};
 916	
 917	Socket.prototype._doQueue = function(){
 918		if (!('_queueStack' in this) || !this._queueStack.length) return this;
 919		this.transport.send(this._queueStack);
 920		this._queueStack = [];
 921		return this;
 922	};
 923	
 924	Socket.prototype._isXDomain = function(){
 925    var locPort = window.location.port || 80;
 926		return this.host !== document.domain || this.options.port != locPort;
 927	};
 928	
 929	Socket.prototype._onConnect = function(){
 930		this.connected = true;
 931		this.connecting = false;
 932		this._doQueue();
 933		if (this.options.rememberTransport) this.options.document.cookie = 'socketio=' + encodeURIComponent(this.transport.type);
 934		this.emit('connect');
 935	};
 936	
 937	Socket.prototype._onMessage = function(data){
 938		this.emit('message', [data]);
 939	};
 940	
 941	Socket.prototype._onDisconnect = function(){
 942		var wasConnected = this.connected;
 943		this.connected = false;
 944		this.connecting = false;
 945		this._queueStack = [];
 946		if (wasConnected){
 947			this.emit('disconnect');
 948			if (this.options.reconnect && !this.reconnecting) this._onReconnect();
 949		}
 950	};
 951	
 952	Socket.prototype._onReconnect = function(){
 953		this.reconnecting = true;
 954		this.reconnectionAttempts = 0;
 955		this.reconnectionDelay = this.options.reconnectionDelay;
 956		
 957		var self = this
 958			, tryTransportsOnConnectTimeout = this.options.tryTransportsOnConnectTimeout
 959			, rememberTransport = this.options.rememberTransport;
 960		
 961		function reset(){
 962			if(self.connected) self.emit('reconnect',[self.transport.type,self.reconnectionAttempts]);
 963			self.removeEvent('connect_failed', maybeReconnect).removeEvent('connect', maybeReconnect);
 964			delete self.reconnecting;
 965			delete self.reconnectionAttempts;
 966			delete self.reconnectionDelay;
 967			delete self.reconnectionTimer;
 968			delete self.redoTransports;
 969			self.options.tryTransportsOnConnectTimeout = tryTransportsOnConnectTimeout;
 970			self.options.rememberTransport = rememberTransport;
 971			
 972			return;
 973		};
 974		
 975		function maybeReconnect(){
 976			if (!self.reconnecting) return;
 977			if (!self.connected){
 978				if (self.connecting && self.reconnecting) return self.reconnectionTimer = setTimeout(maybeReconnect, 1000);
 979				
 980				if (self.reconnectionAttempts++ >= self.options.maxReconnectionAttempts){
 981					if (!self.redoTransports){
 982						self.on('connect_failed', maybeReconnect);
 983						self.options.tryTransportsOnConnectTimeout = true;
 984						self.transport = self.getTransport(self.options.transports); // overwrite with all enabled transports
 985						self.redoTransports = true;
 986						self.connect();
 987					} else {
 988						self.emit('reconnect_failed');
 989						reset();
 990					}
 991				} else {
 992					self.reconnectionDelay *= 2; // exponential backoff
 993					self.connect();
 994					self.emit('reconnecting', [self.reconnectionDelay,self.reconnectionAttempts]);
 995					self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay);
 996				}
 997			} else {
 998				reset();
 999			}
1000		};
1001		this.options.tryTransportsOnConnectTimeout = false;
1002		this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay);
1003		
1004		this.on('connect', maybeReconnect);
1005	};
1006
1007  Socket.prototype.fire = Socket.prototype.emit;
1008	Socket.prototype.addListener = Socket.prototype.addEvent = Socket.prototype.addEventListener = Socket.prototype.on;
1009	Socket.prototype.removeListener = Socket.prototype.removeEventListener = Socket.prototype.removeEvent;
1010	
1011})();
1012/*	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
1013	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
1014*/
1015var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
1016// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
1017// License: New BSD License
1018// Reference: http://dev.w3.org/html5/websockets/
1019// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
1020
1021(function() {
1022  
1023  if (window.WebSocket) return;
1024
1025  var console = window.console;
1026  if (!console || !console.log || !console.error) {
1027    console = {log: function(){ }, error: function(){ }};
1028  }
1029  
1030  if (!swfobject.hasFlashPlayerVersion("10.0.0")) {
1031    console.error("Flash Player >= 10.0.0 is required.");
1032    return;
1033  }
1034  if (location.protocol == "file:") {
1035    console.error(
1036      "WARNING: web-socket-js doesn't work in file:///... URL " +
1037      "unless you set Flash Security Settings properly. " +
1038      "Open the page via Web server i.e. http://...");
1039  }
1040
1041  /**
1042   * This class represents a faux web socket.
1043   * @param {string} url
1044   * @param {string} protocol
1045   * @param {string} proxyHost
1046   * @param {int} proxyPort
1047   * @param {string} headers
1048   */
1049  WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
1050    var self = this;
1051    self.__id = WebSocket.__nextId++;
1052    WebSocket.__instances[self.__id] = self;
1053    self.readyState = WebSocket.CONNECTING;
1054    self.bufferedAmount = 0;
1055    // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
1056    // Otherwise, when onopen fires immediately, onopen is called before it is set.
1057    setTimeout(function() {
1058      WebSocket.__addTask(function() {
1059        WebSocket.__flash.create(
1060            self.__id, url, protocol, proxyHost || null, proxyPort || 0, headers || null);
1061      });
1062    }, 0);
1063  };
1064
1065  /**
1066   * Send data to the web socket.
1067   * @param {string} data  The data to send to the socket.
1068   * @return {boolean}  True for success, false for failure.
1069   */
1070  WebSocket.prototype.send = function(data) {
1071    if (this.readyState == WebSocket.CONNECTING) {
1072      throw "INVALID_STATE_ERR: Web Socket connection has not been established";
1073    }
1074    // We use encodeURIComponent() here, because FABridge doesn't work if
1075    // the argument includes some characters. We don't use escape() here
1076    // because of this:
1077    // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
1078    // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
1079    // preserve all Unicode characters either e.g. "\uffff" in Firefox.
1080    // Note by wtritch: Hopefully this will not be necessary using ExternalInterface.  Will require
1081    // additional testing.
1082    var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
1083    if (result < 0) { // success
1084      return true;
1085    } else {
1086      this.bufferedAmount += result;
1087      return false;
1088    }
1089  };
1090
1091  /**
1092   * Close this web socket gracefully.
1093   */
1094  WebSocket.prototype.close = function() {
1095    if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
1096      return;
1097    }
1098    this.readyState = WebSocket.CLOSING;
1099    WebSocket.__flash.close(this.__id);
1100  };
1101
1102  /**
1103   * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
1104   *
1105   * @param {string} type
1106   * @param {function} listener
1107   * @param {boolean} useCapture !NB Not implemented yet
1108   * @return void
1109   */
1110  WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
1111    if (!('__events' in this)) {
1112      this.__events = {};
1113    }
1114    if (!(type in this.__events)) {
1115      this.__events[type] = [];
1116      if ('function' == typeof this['on' + type]) {
1117        this.__events[type].defaultHandler = this['on' + type];
1118        this['on' + type] = this.__createEventHandler(this, type);
1119      }
1120    }
1121    this.__events[type].push(listener);
1122  };
1123
1124  /**
1125   * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
1126   *
1127   * @param {string} type
1128   * @param {function} listener
1129   * @param {boolean} useCapture NB! Not implemented yet
1130   * @return void
1131   */
1132  WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
1133    if (!('__events' in this)) {
1134      this.__events = {};
1135    }
1136    if (!(type in this.__events)) return;
1137    for (var i = this.__events.length; i > -1; --i) {
1138      if (listener === this.__events[type][i]) {
1139        this.__events[type].splice(i, 1);
1140        break;
1141      }
1142    }
1143  };
1144
1145  /**
1146   * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
1147   *
1148   * @param {WebSocketEvent} event
1149   * @return void
1150   */
1151  WebSocket.prototype.dispatchEvent = function(event) {
1152    if (!('__events' in this)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
1153    if (!(event.type in this.__events)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
1154  
1155    for (var i = 0, l = this.__events[event.type].length; i < l; ++ i) {
1156      this.__events[event.type][i](event);
1157      if (event.cancelBubble) break;
1158    }
1159  
1160    if (false !== event.returnValue &&
1161      'function' == typeof this.__events[event.type].defaultHandler)
1162    {
1163      this.__events[event.type].defaultHandler(event);
1164    }
1165  };
1166
1167  /**
1168   * Handle an event from flash.  Do any websocket-specific
1169   * handling before passing the event off to the event handlers.
1170   * @param {Object} event
1171   */
1172  WebSocket.prototype.__handleEvent = function(event) {
1173    if ("readyState" in event) {
1174      this.readyState = event.readyState;
1175    }
1176  
1177    try {
1178      if (event.type == "open") {
1179        this.onopen && this.onopen();
1180      } else if (event.type == "close") {
1181        this.onclose && this.onclose();
1182      } else if (event.type == "error") {
1183        this.onerror && this.onerror(event);
1184      } else if (event.type == "message") {
1185        if (this.onmessage) {
1186          var data = decodeURIComponent(event.message);
1187          var e;
1188          if (window.MessageEvent && !window.opera) {
1189            e = document.createEvent("MessageEvent");
1190            e.initMessageEvent("message", false, false, data, null, null, window, null);
1191          } else {
1192            // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
1193            e = {data: data};
1194          }
1195          this.onmessage(e);
1196        }
1197        
1198      } else {
1199        throw "unknown event type: " + event.type;
1200      }
1201    } catch (e) {
1202      console.error(e.toString());
1203    }
1204  };
1205  
1206  /**
1207   * @param {object} object
1208   * @param {string} type
1209   */
1210  WebSocket.prototype.__createEventHandler = function(object, type) {
1211    return function(data) {
1212      var event = new WebSocketEvent();
1213      event.initEvent(type, true, true);
1214      event.target = event.currentTarget = object;
1215      for (var key in data) {
1216        event[key] = data[key];
1217      }
1218      object.dispatchEvent(event, arguments);
1219    };
1220  };
1221
1222  /**
1223   * Define the WebSocket readyState enumeration.
1224   */
1225  WebSocket.CONNECTING = 0;
1226  WebSocket.OPEN = 1;
1227  WebSocket.CLOSING = 2;
1228  WebSocket.CLOSED = 3;
1229
1230  WebSocket.__flash = null;
1231  WebSocket.__instances = {};
1232  WebSocket.__tasks = [];
1233  WebSocket.__nextId = 0;
1234  
1235  /**
1236   * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
1237   */
1238  WebSocket.__initialize = function() {
1239    if (WebSocket.__flash) return;
1240    
1241    if (WebSocket.__swfLocation) {
1242      // For backword compatibility.
1243      window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
1244    }
1245    if (!window.WEB_SOCKET_SWF_LOCATION) {
1246      console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
1247      return;
1248    }
1249    var container = document.createElement("div");
1250    container.id = "webSocketContainer";
1251    // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
1252    // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
1253    // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
1254    // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
1255    // the best we can do as far as we know now.
1256    container.style.position = "absolute";
1257    if (WebSocket.__isFlashLite()) {
1258      container.style.left = "0px";
1259      container.style.top = "0px";
1260    } else {
1261      container.style.left = "-100px";
1262      container.style.top = "-100px";
1263    }
1264    var holder = document.createElement("div");
1265    holder.id = "webSocketFlash";
1266    container.appendChild(holder);
1267    document.body.appendChild(container);
1268    // See this article for hasPriority:
1269    // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
1270    swfobject.embedSWF(
1271      WEB_SOCKET_SWF_LOCATION,
1272      "webSocketFlash",
1273      "1" /* width */,
1274      "1" /* height */,
1275      "10.0.0" /* SWF version */,
1276      null,
1277      null,
1278      {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
1279      null,
1280      function(e) {
1281        if (!e.success) {
1282          console.error("[WebSocket] swfobject.embedSWF failed");
1283        }
1284      });
1285  };
1286  
1287  /**
1288   * Load a new flash security policy file.
1289   * @param {string} url
1290   */
1291  WebSocket.loadFlashPolicyFile = function(url){
1292    WebSocket.__addTask(function() {
1293      WebSocket.__flash.loadManualPolicyFile(url);
1294    });
1295  };
1296
1297  /**
1298   * Called by flash to notify js that it's fully loaded and ready
1299   * for communication.
1300   */
1301  WebSocket.__onFlashInitialized = function() {
1302    // We need to set a timeout here to avoid round-trip calls
1303    // to flash during the initialization process.
1304    setTimeout(function() {
1305      WebSocket.__flash = document.getElementById("webSocketFlash");
1306      WebSocket.__flash.setCallerUrl(location.href);
1307      WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
1308      for (var i = 0; i < WebSocket.__tasks.length; ++i) {
1309        WebSocket.__tasks[i]();
1310      }
1311      WebSocket.__tasks = [];
1312    }, 0);
1313  };
1314  
1315  /**
1316   * Called by flash to dispatch an event to a web socket.
1317   * @param {object} eventObj  A web socket event dispatched from flash.
1318   */
1319  WebSocket.__onFlashEvent = function() {
1320    setTimeout(function() {
1321      // Gets events using receiveEvents() instead of getting it from event object
1322      // of Flash event. This is to make sure to keep message order.
1323      // It seems sometimes Flash events don't arrive in the same order as they are sent.
1324      var events = WebSocket.__flash.receiveEvents();
1325      for (var i = 0; i < events.length; ++i) {
1326        WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
1327      }
1328    }, 0);
1329    return true;
1330  };
1331  
1332  // called from Flash
1333  WebSocket.__log = function(message) {
1334    console.log(decodeURIComponent(message));
1335  };
1336  
1337  // called from Flash
1338  WebSocket.__error = function(message) {
1339    console.error(decodeURIComponent(message));
1340  };
1341  
1342  WebSocket.__addTask = function(task) {
1343    if (WebSocket.__flash) {
1344      task();
1345    } else {
1346      WebSocket.__tasks.push(task);
1347    }
1348  };
1349  
1350  /**
1351   * Test if the browser is running flash lite.
1352   * @return {boolean} True if flash lite is running, false otherwise.
1353   */
1354  WebSocket.__isFlashLite = function() {
1355    if (!window.navigator || !window.navigator.mimeTypes) {
1356      return false;
1357    }
1358    var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
1359    if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
1360      return false;
1361    }
1362    return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
1363  };
1364  
1365  /**
1366   * Basic implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface">DOM 2 EventInterface</a>}
1367   *
1368   * @class
1369   * @constructor
1370   */
1371  function WebSocketEvent(){}
1372  
1373  /**
1374   *
1375   * @type boolean
1376   */
1377  WebSocketEvent.prototype.cancelable = true;
1378  
1379  /**
1380  *
1381  * @type boolean
1382  */
1383  WebSocketEvent.prototype.cancelBubble = false;
1384  
1385  /**
1386  *
1387  * @return void
1388  */
1389  WebSocketEvent.prototype.preventDefault = function() {
1390    if (this.cancelable) {
1391      this.returnValue = false;
1392    }
1393  };
1394  
1395  /**
1396  *
1397  * @return void
1398  */
1399  WebSocketEvent.prototype.stopPropagation = function() {
1400    this.cancelBubble = true;
1401  };
1402
1403  /**
1404  *
1405  * @param {string} eventTypeArg
1406  * @param {boolean} canBubbleArg
1407  * @param {boolean} cancelableArg
1408  * @return void
1409  */
1410  WebSocketEvent.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) {
1411    this.type = eventTypeArg;
1412    this.cancelable = cancelableArg;
1413    this.timeStamp = new Date();
1414  };
1415
1416  if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
1417    if (window.addEventListener) {
1418      window.addEventListener("load", function(){
1419        WebSocket.__initialize();
1420      }, false);
1421    } else {
1422      window.attachEvent("onload", function(){
1423        WebSocket.__initialize();
1424      });
1425    }
1426  }
1427  
1428})();
1429