//  <JasobNoObfs>
//	---------------------------------------------------------------------------
//	jWebSocket Client (uses jWebSocket Server)
//	Copyright (c) 2010 Alexander Schulze, Innotrade GmbH, Herzogenrath
//	---------------------------------------------------------------------------
//	This program is free software; you can redistribute it and/or modify it
//	under the terms of the GNU Lesser General Public License as published by the
//	Free Software Foundation; either version 3 of the License, or (at your
//	option) any later version.
//	This program is distributed in the hope that it will be useful, but WITHOUT
//	ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//	FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
//	more details.
//	You should have received a copy of the GNU Lesser General Public License along
//	with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>.
//	---------------------------------------------------------------------------
//  </JasobNoObfs>

// ## :#file:*:jWebSocket.js
// ## :#d:en:Implements the jWebSocket Web Client.


// Firefox temporarily used MozWebSocket (why?), anyway, consider this here.
// Since the browserSupportNativeWebSocket method evaluates the existance of
// the window.WebSocket class, this abstraction need to be done on the very top.
// please do not move this lines down.
if( window.MozWebSocket ) {
	window.WebSocket = window.MozWebSocket;
}

//:package:*:jws
//:class:*:jws
//:ancestor:*:-
//:d:en:Implements the basic "jws" name space for the jWebSocket client
//:d:en:including various utility methods.
var jws = {

	//:const:*:VERSION:String:1.0b1 (10902)
	//:d:en:Version of the jWebSocket JavaScript Client
	VERSION: "1.0b1 (10902)",

	//:const:*:NS_BASE:String:org.jwebsocket
	//:d:en:Base namespace
	NS_BASE: "org.jwebsocket",
	NS_SYSTEM: "org.jwebsocket.plugins.system",
	
	MSG_WS_NOT_SUPPORTED:
		"Unfortunately your browser does neither natively support WebSockets\n" +
		"nor you have the Adobe Flash-PlugIn 10+ installed.\n" +
		"Please download the last recent Adobe Flash Player at http://get.adobe.com/flashplayer.",

	// some namespace global constants
	
	//:const:*:CUR_TOKEN_ID:Integer:0
	//:d:en:Current token id, incremented per token exchange to assign results.
	CUR_TOKEN_ID: 0,
	//:const:*:JWS_SERVER_SCHEMA:String:ws
	//:d:en:Default schema, [tt]ws[/tt] for un-secured WebSocket-Connections.
	JWS_SERVER_SCHEMA: "ws",
	//:const:*:JWS_SERVER_SSL_SCHEMA:String:wss
	//:d:en:Default schema, [tt]wss[/tt] for secured WebSocket-Connections.
	JWS_SERVER_SSL_SCHEMA: "wss",
	//:const:*:JWS_SERVER_HOST:String:[hostname|localhost|IP-Number]
	//:d:en:Default hostname of current webbite or [tt]localhost|127.0.0.1[/tt] if no hostname can be detected.
	JWS_SERVER_HOST: ( self.location.hostname ? self.location.hostname : "127.0.0.1" ),
	//:const:*:JWS_SERVER_PORT:Integer:8787
	//:d:en:Default port number, 8787 for stand-alone un-secured servers, _
	//:d:en:80 for Jetty or Glassfish un-secured servers.
	JWS_SERVER_PORT: 8787,
	//:const:*:JWS_SERVER_SSL_PORT:Integer:9797
	//:d:en:Default port number, 9797 for stand-alone SSL secured servers, _
	//:d:en:443 for Jetty or Glassfish SSL secured servers.
	JWS_SERVER_SSL_PORT: 9797,
	//:const:*:JWS_SERVER_CONTEXT:String:jWebSocket
	//:d:en:Default application context in web application servers or servlet containers like Jetty or GlassFish.
	JWS_SERVER_CONTEXT: "/jWebSocket",
	//:const:*:JWS_SERVER_SERVLET:String:jWebSocket
	//:d:en:Default servlet in web application servers or servlet containers like Jetty or GlassFish.
	JWS_SERVER_SERVLET: "/jWebSocket",
	//:const:*:JWS_SERVER_URL:String:ws://[hostname]/jWebSocket/jWebSocket:8787
	//:d:en:Current token id, incremented per token exchange to assign results.
	//:@deprecated:en:Use [tt]getDefaultServerURL()[/tt] instead.
	JWS_SERVER_URL:
		"ws://" + ( self.location.hostname ? self.location.hostname : "127.0.0.1" ) + ":8787/jWebSocket/jWebSocket",

	//:const:*:CONNECTING:Integer:0
	//:d:en:The connection has not yet been established.
	CONNECTING: 0,
	//:const:*:OPEN:Integer:1
	//:d:en:The WebSocket connection is established and communication is possible.
	OPEN: 1,
	//:const:*:CLOSING:Integer:2
	//:d:en:The connection is going through the closing handshake.
	CLOSING: 2,
	//:const:*:CLOSED:Integer:3
	//:d:en:The connection has been closed or could not be opened.
	CLOSED: 3,

	//:const:*:RECONNECTING:Integer:1000
	//:d:en:The connection manager is trying to re-connect, but not yet connected. _
	//:d:en:This is jWebSocket specific and not part of the W3C API.
	RECONNECTING: 1000,
	//:const:*:OPEN_TIMED_OUT:Integer:1001
	//:d:en:The connection could not be established within the given timeout. _
	//:d:en:This is jWebSocket specific and not part of the W3C API.
	OPEN_TIMED_OUT: 1001,

	// Default connection reliability options
	RO_OFF: {
		autoReconnect : false,
		reconnectDelay: -1,
		reconnectTimeout: -1,
		queueItemLimit: -1,
		queueSizeLimit: -1
	},

	RO_ON: {
		autoReconnect: true,
		reconnectDelay: 500,
		reconnectTimeout: 30000,
		queueItemLimit: 1000,
		queueSizeLimit: 1024 * 1024 * 10 // 10 MByte
	},
	
	//:const:*:WS_SUBPROT_JSON:String:org.jwebsocket.json
	//:d:en:jWebSocket sub protocol JSON
	WS_SUBPROT_JSON: "org.jwebsocket.json",
	//:const:*:WS_SUBPROT_XML:String:org.jwebsocket.xml
	//:d:en:jWebSocket sub protocol XML
	WS_SUBPROT_XML: "org.jwebsocket.xml",
	//:const:*:WS_SUBPROT_CSV:String:org.jwebsocket.csv
	//:d:en:jWebSocket sub protocol CSV
	WS_SUBPROT_CSV: "org.jwebsocket.csv",
	//:const:*:WS_SUBPROT_CUSTOM:String:org.jwebsocket.text
	//:d:en:jWebSocket sub protocol text
	//:@deprecated:en:Use [tt]WS_SUBPROT_TEXT()[/tt] instead.
	WS_SUBPROT_CUSTOM: "org.jwebsocket.text",
	//:const:*:WS_SUBPROT_TEXT:String:org.jwebsocket.text
	//:d:en:jWebSocket sub protocol text
	WS_SUBPROT_TEXT: "org.jwebsocket.text",
	//:const:*:WS_SUBPROT_BINARY:String:org.jwebsocket.binary
	//:d:en:jWebSocket sub protocol binary
	WS_SUBPROT_BINARY: "org.jwebsocket.binary",

	//:const:*:SCOPE_PRIVATE:String:private
	//:d:en:private scope, only authenticated user can read and write his personal items
	SCOPE_PRIVATE: "private",
	//:const:*:SCOPE_PUBLIC:String:public
	//:d:en:public scope, everybody can read and write items from this scope
	SCOPE_PUBLIC: "public",

	//:const:*:DEF_RESP_TIMEOUT:integer:30000
	//:d:en:Default timeout in milliseconds for waiting on asynchronous responses.
	//:d:en:An individual timeout can be passed per request.
	DEF_RESP_TIMEOUT: 30000,


	//:i:en:Browsertype Constants
	//:const:*:BT_UNKNOWN
	//:d:en:Browsertype is unknown.
	BT_UNKNOWN		:  0,
	//:const:*:BT_FIREFOX
	//:d:en:Browser is "Firefox".
	BT_FIREFOX		:  1,
	//:const:*:BT_NETSCAPE
	//:d:en:Browser is "Netscape".
	BT_NETSCAPE		:  2,
	//:const:*:BT_OPERA
	//:d:en:Browser is "Opera".
	BT_OPERA		:  3,
	//:const:*:BT_IEXPLORER
	//:d:en:Browser is "Internet Explorer".
	BT_IEXPLORER	:  4,
	//:const:*:BT_SAFARI
	//:d:en:Browser is "Safari".
	BT_SAFARI		:  5,
	//:const:*:BT_CHROME
	//:d:en:Browser is "Chrome".
	BT_CHROME		: 6,

	//:const:*:BROWSER_NAMES
	//:d:en:Array of browser names. Each BT_xxx constant can be used as an index to this array.
	BROWSER_NAMES : [
		"Unknown",
		"Firefox",
		"Netscape",
		"Opera",
		"Internet Explorer",
		"Safari",
		"Chrome"
	],
	
	//:const:*:GUEST_USER_LOGINNAME:String:guest
	//:d:en:Guest user login name is "guest" (if not changed on the server).
	GUEST_USER_LOGINNAME: "guest",
	//:const:*:GUEST_USER_PASSWORD:String:guest
	//:d:en:Guest user password is "guest" (if not changed on the server).
	GUEST_USER_PASSWORD: "guest",

	//:const:*:DEMO_ADMIN_LOGINNAME:String:root
	//:d:en:Root user login name is "root" (if not changed on the server).
	//:d:en:FOR DEMO AND DEBUG PURPOSES ONLY! NEVER SAVE PRODUCTION ROOT CREDENTIALS HERE!
	DEMO_ROOT_LOGINNAME: "root",
	//:const:*:DEMO_ADMIN_PASSWORD:String:root
	//:d:en:Root user password is "root" (if not changed on the server).
	//:d:en:FOR DEMO AND DEBUG PURPOSES ONLY! NEVER SAVE PRODUCTION ROOT CREDENTIALS HERE!
	DEMO_ROOT_PASSWORD: "root",
	
	//:m:*:$
	//:d:en:Convenience replacement for [tt]document.getElementById()[/tt]. _
	//:d:en:Returns the first HTML element with the given id or [tt]null[/tt] _
	//:d:en:if the element could not be found.
	//:a:en::aId:String:id of the HTML element to be returned.
	//:r:*:::void:none
	$: function( aId ) {
		return document.getElementById( aId );
	},

	//:m:*:getServerURL
	//:d:en:Returns the URL to the jWebSocket based on schema, host, port, _
	//:d:en:context and servlet.
	//:a:en::voide::
	//:r:*:::void:jWebSocket server URL consisting of schema://host:port/context/servlet
	getServerURL: function( aSchema, aHost, aPort, aContext, aServlet ) {
		var lURL =
			aSchema + "://"
			+ aHost 
			+ ( aPort ? ":" + aPort : "" );
		if( aContext && aContext.length > 0 ) {
			lURL += aContext;
			if( aServlet && aServlet.length > 0 ) {
				lURL += aServlet;
			}
		}
		return lURL;
	},

	//:m:*:getDefaultServerURL
	//:d:en:Returns the default URL to the un-secured jWebSocket Server. This is a convenience _
	//:d:en:method used in all jWebSocket demo dialogs. In case of changes to the _
	//:d:en:server URL you only need to change to above JWS_SERVER_xxx constants.
	//:a:en::voide::
	//:r:*:::void:Default jWebSocket server URL consisting of schema://host:port/context/servlet
	getDefaultServerURL: function() {
		return( this.getServerURL(
			jws.JWS_SERVER_SCHEMA,
			jws.JWS_SERVER_HOST,
			jws.JWS_SERVER_PORT,
			jws.JWS_SERVER_CONTEXT,
			jws.JWS_SERVER_SERVLET
		));
	},

	//:m:*:getDefaultSSLServerURL
	//:d:en:Returns the default URL to the secured jWebSocket Server. This is a convenience _
	//:d:en:method used in all jWebSocket demo dialogs. In case of changes to the _
	//:d:en:server URL you only need to change to above JWS_SERVER_xxx constants.
	//:a:en::voide::
	//:r:*:::void:Default jWebSocket server URL consisting of schema://host:port/context/servlet
	getDefaultSSLServerURL: function() {
		return( this.getServerURL(
			jws.JWS_SERVER_SSL_SCHEMA,
			jws.JWS_SERVER_HOST,
			jws.JWS_SERVER_SSL_PORT,
			jws.JWS_SERVER_CONTEXT,
			jws.JWS_SERVER_SERVLET
		));
	},

	//:m:*:browserSupportsWebSockets
	//:d:en:checks if the browser or one of its plug-ins like flash or chrome _
	//:d:en:do support web sockets to be used by an application.
	//:a:en::::none
	//:r:*:::boolean:true if the browser or one of its plug-ins support websockets, otherwise false.
	browserSupportsWebSockets: function() {
		return( 
			window.WebSocket !== null && window.WebSocket !== undefined
		);
	},

	//:m:*:browserSupportsNativeWebSockets
	//:d:en:checks if the browser natively supports web sockets, no plug-ins
	//:d:en:are considered. Caution! This is a public field not a function!
	//:a:en::::none
	//:r:*:::boolean:true if the browser natively support websockets, otherwise false.
	browserSupportsNativeWebSockets: (function() {
		return(
			window.WebSocket !== null && window.WebSocket !== undefined
		);
	})(),

	//:m:*:browserSupportsJSON
	//:d:en:checks if the browser natively or by JSON lib does support JSON.
	//:a:en::::none
	//:r:*:::boolean:true if the browser or one of its plug-ins support JSON, otherwise false.
	browserSupportsJSON: function() {
		return(
			window.JSON !== null && window.JSON !== undefined
		);
	},

	//:m:*:browserSupportsNativeJSON
	//:d:en:checks if the browser natively supports JSON, no plug-ins
	//:d:en:are considered. Caution! This is a public field not a function!
	//:a:en::::none
	//:r:*:::boolean:true if the browser natively support websockets, otherwise false.
	browserSupportsNativeJSON: (function() {
		return(
			window.JSON !== null && window.JSON !== undefined
		);
	})(),

	//:m:*:browserSupportsWebWorkers
	//:d:en:checks if the browser natively supports HTML5 WebWorkers
	//:a:en::::none
	//:r:*:::boolean:true if the browser natively support WebWorkers, otherwise false.
	browserSupportsWebWorkers: (function() {
		return(
			window.Worker !== null && window.Worker !== undefined
		);
	})(),

	//:m:*:runAsThread
	//:d:en:checks if the browser natively supports HTML5 WebWorkers
	//:a:en::::none
	//:r:*:::boolean:true if the browser natively support WebWorkers, otherwise false.
	runAsThread: function( aOptions ) {
		// if browser does not support WebWorkers nothing can be done here
		if ( !this.browserSupportsWebWorkers ) {
			return {
				code: -1,
				msg: "Browser does not (yet) support WebWorkers."
			};
		}
		// check if options were passed
		if( !aOptions ) {
			aOptions = {};
		}
		// set default options
		var lOnMessage = null;
		var lOnError = null;
		var lFile = jws.SCRIPT_PATH + "jwsWorker.js";
		var lMethod = null;
		var lArgs = [];
		// checked options passed
		if( aOptions.OnMessage && typeof aOptions.OnMessage == "function" ) {
			lOnMessage = aOptions.OnMessage;
		}
		if( aOptions.OnError && typeof aOptions.OnError == "function" ) {
			lOnError = aOptions.OnError;
		}
		if( aOptions.file && typeof aOptions.file == "String" ) {
			lFile = aOptions.file;
		}
		if( aOptions.method && typeof aOptions.method == "function" ) {
			lMethod = aOptions.method;
		}
		if( aOptions.args ) {
			lArgs = aOptions.args;
		}
		// TODO:
		// check lArgs for type, if needed convert to array

		var lThis = this;
		// create worker object if required
		if( !jws.worker ) {
			jws.worker = new Worker( lFile );

			// This listener is called when a message from the thread
			// to the application is posted.
			jws.worker.onmessage = function( aEvent ) {
				if( lOnMessage != null ) {
					lOnMessage.call( lThis, {
						data: aEvent.data
					});
				}
				// console.log( "Worker message: " + JSON.stringify( aEvent.data ) );
			};

			// This listener is called when an error
			// occurred within the thread.
			jws.worker.onerror = function( aEvent ) {
				if( lOnError != null ) {
					lOnError.call( lThis, {
						message: aEvent.message
					});
				}
				// console.log( "Worker error: " + aEvent.message );
			};
		}

		jws.worker.postMessage({
			// instance: lThis,
			method: lMethod.toString(),
			args: lArgs
		});

		return {
			code: 0,
			msg: "ok",
			worker: jws.worker
		};
	},

	SCRIPT_PATH: (function() {
		var lScripts = document.getElementsByTagName( "script" );
		for( var lIdx = 0, lCnt = lScripts.length; lIdx < lCnt; lIdx++ ) {
			var lScript = lScripts[ lIdx ];
			var lPath = lScript.src;
			if( !lPath ) {
				lPath = lScript.getAttribute( "src" );
			}
			if( lPath ) {
				var lPos = lPath.lastIndexOf( "jWebSocket" );
				if( lPos > 0 ) {
					return lPath.substr( 0, lPos );
				}
			}
		}
		return null;
	})(),

	//:m:*:isIE
	//:d:en:checks if the browser is Internet Explorer. _
	//:d:en:This is needed to switch to IE specific event model.
	//:a:en::::none
	//:r:*:::boolean:true if the browser is IE, otherwise false.
	isIE: (function() {
		var lUserAgent = navigator.userAgent;
		var lIsIE = lUserAgent.indexOf( "MSIE" );
		return( lIsIE >= 0 );
	})(),

	//:i:de:Bei Erweiterung der Browsertypen auch BROWSER_NAMES entsprechend anpassen!

	//:m:*:getBrowserName
	//:d:de:Liefert den Namen des aktuell verwendeten Browser zur&uuml;ck.
	//:d:en:Returns the name of the browser.
	//:a:*::-
	//:r:de::browserName:String:Name des verwendeten Broswers.
	//:r:en::browserName:String:Name of the used browser.
	getBrowserName: function() {
		return this.fBrowserName;
	},

	//:m:*:getBrowserVersion
	//:d:de:Liefert die Browserversion als Flie&szlig;kommazahl zur&uuml;ck.
	//:d:en:Returns the browser version als float value.
	//:a:*::-
	//:r:de::browserVersion:Float:Die Versions Nummer des Browsers.
	//:r:en::browserVersion:Float:Version number of the browser.
	getBrowserVersion: function() {
		return this.fBrowserVerNo;
	},

	//:m:*:getBrowserVersionString
	//:d:de:Liefert die Browserversion als String zur&uuml;ck.
	//:d:en:Returns the browser version as string value.
	//:a:*::-
	//:r:de:::String:Die Versions Nummer des Browsers als String.
	//:r:en:::String:Version string of the browser.
	getBrowserVersionString: function() {
		return this.fBrowserVerStr;
	},

	//:m:*:isFirefox
	//:d:de:Ermittelt, ob der verwendete Browser von Typ "Firefox" ist.
	//:d:en:Determines, if the used browser is a "Firefox".
	//:a:*::-
	//:r:de::isFirefox:Boolean:true, wenn der Browser Firefox ist, andernfalls false.
	//:r:en::isFirefox:Boolean:true, if Browser is Firefox, otherwise false.
	isFirefox: function() {
		return this.fIsFirefox;
	},

	//:m:*:isOpera
	//:d:de:Ermittelt, ob der verwendete Browser von Typ "Opera" ist.
	//:d:en:Determines, if the used browser is a "Opera".
	//:a:*::-
	//:r:de::isOpera:Boolean:true, wenn der Browser Opera ist, andernfalls false.
	//:r:en::isOpera:Boolean:true, if Browser is Opera, otherwise false.
	isOpera: function() {
		return this.fIsOpera;
	},

	//:m:*:isChrome
	//:d:de:Ermittelt, ob der verwendete Browser von Typ "Chrome" ist.
	//:d:en:Determines, if the used browser is a "Chrome".
	//:a:*::-
	//:r:de::isOpera:Boolean:true, wenn der Browser Chrome ist, andernfalls false.
	//:r:en::isOpera:Boolean:true, if Browser is Chrome, otherwise false.
	isChrome: function() {
		return this.fIsChrome;
	},

	//:m:*:isIExplorer
	//:d:de:Ermittelt, ob der verwendete Browser von Typ "Internet Explorer" ist.
	//:d:en:Determines, if the used browser is a "Internet Explorer".
	//:a:*::-
	//:r:de::isIExplorer:Boolean:true, wenn der Browser Internet Explorer ist, andernfalls false.
	//:r:en::isIExplorer:Boolean:true, if Browser is Internet Explorer, otherwise false.
	isIExplorer: function() {
		return this.fIsIExplorer;
	},

	isIE_LE6: function() {
		return( this.isIExplorer() && this.getBrowserVersion() < 7 );
	},

	isIE_LE7: function() {
		return( this.isIExplorer() && this.getBrowserVersion() < 8 );
	},

	isIE_GE8: function() {
		return( this.isIExplorer() && this.getBrowserVersion() >= 8 );
	},

	//:m:*:isSafari
	//:d:de:Ermittelt, ob der verwendete Browser von Typ "Safari" ist.
	//:d:en:Determines, if the used browser is a "Safari".
	//:a:*::-
	//:r:de::isSafari:Boolean:true, wenn der Browser Safari ist, andernfalls false.
	//:r:en::isSafari:Boolean:true, if Browser is Safari, otherwise false.
	isSafari: function() {
		return this.fIsSafari;
	},

	//:m:*:isNetscape
	//:d:de:Ermittelt, ob der verwendete Browser von Typ "Netscape" ist.
	//:d:en:Determines, if the used browser is a "Netscape".
	//:a:*::-
	//:r:de:::Boolean:true, wenn der Browser Netscape ist, andernfalls false.
	//:r:en:::Boolean:true, if Browser is Netscape, otherwise false.
	isNetscape: function() {
		return this.fIsNetscape;
	},

	//:m:de:isPocketIE
	//:d:de:...
	//:d:en:...
	//:a:*::-
	//:r:de::isPocketIE:Boolean:true, wenn der Browser Pocket Internet Explorer ist, andernfalls false.
	//:r:en::isPocketIE:Boolean:true, if Browser is Pocket Internet Explorer, otherwise false.
	isPocketIE: function() {
		return this.fIsPocketIE;
	},

	console: {
		
		isActive: false,
		level: 2,
		ALL: 0,
		DEBUG: 1,
		INFO: 2,
		WARN: 3,
		ERROR: 4,
		FATAL: 5,
	
		log: function( aMsg ) {
			if( window.console 
				&& jws.console.isActive
			) {
				console.log( aMsg );
			}
		},
		debug: function( aMsg ) {
			if( window.console
				&& jws.console.isActive 
				&& jws.console.level <= jws.console.DEBUG
			) {
				console.debug( aMsg );
			}
		},
		info: function( aMsg ) {
			if( window.console 
				&& jws.console.isActive 
				&& jws.console.level <= jws.console.INFO
			) {
				console.info( aMsg );
			}
		},
		warn: function( aMsg ) {
			if( window.console
				&& jws.console.isActive
				&& jws.console.level <= jws.console.WARN
			) {
				console.warn( aMsg );
			}
		},
		error: function( aMsg ) {
			if( window.console
				&& jws.console.isActive
				&& jws.console.level <= jws.console.ERROR
			) {
				console.error( aMsg );
			}
		},
		fatal: function( aMsg ) {
			if( window.console
				&& jws.console.isActive
				&& jws.console.level <= jws.console.FATAL
			) {
				console.fatal( aMsg );
			}
		}
	}	

};


//i:en:Browser detection (embedded into a function to not polute global namespace...
(function() {

	jws.fBrowserName	= "unknown";
	jws.fBrowserType	= jws.BT_UNKNOWN;
	jws.fBrowserVerNo	= undefined;

	jws.fIsIExplorer	= false;
	jws.fIsFirefox		= false;
	jws.fIsNetscape		= false;
	jws.fIsOpera		= false;
	jws.fIsSafari		= false;
	jws.fIsChrome		= false;
	
	var lUA = navigator.userAgent;

	//:i:en:First evaluate name of the browser
	jws.fIsChrome = lUA.indexOf( "Chrome" ) >= 0;
	if( jws.fIsChrome ) {
		jws.fBrowserType = jws.BT_CHROME;
	} else {
		jws.fIsSafari = lUA.indexOf( "Safari" ) >= 0;
		if( jws.fIsSafari ) {
			jws.fBrowserType = jws.BT_SAFARI;
		}
		else {
			jws.fIsNetscape = lUA.indexOf( "Netscape" ) >= 0;
			if( jws.fIsNetscape ) {
				jws.fBrowserType = jws.BT_NETSCAPE;
			} else {
				jws.fIsFirefox = navigator.appName == "Netscape";
				if( jws.fIsFirefox ) {
					jws.fBrowserType = jws.BT_FIREFOX;
				} else {
					jws.fIsOpera = navigator.appName == "Opera";
					if( jws.fIsOpera ) {
						jws.fBrowserType = jws.BT_OPERA;
					} else {
						jws.fIsIExplorer = navigator.appName == "Microsoft Internet Explorer";
						if( jws.fIsIExplorer ) {
							jws.fBrowserType = jws.BT_IEXPLORER;
						} else {
							jws.fIsPocketIE = navigator.appName == "Microsoft Pocket Internet Explorer";
							if( jws.fIsPocketIE ) {
								jws.fBrowserType = jws.BT_IEXPLORER;
							}
						}
					}
				}
			}
		}
	}

	var p, i;
	var lStr;
	var lFound;
	var lVersion;

	if( jws.fIsIExplorer ) {
		//:i:de:Beispiel f&uuml;r userAgent bei IE6:
		//:i:de:"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"
		jws.fBrowserName = jws.BROWSER_NAMES[ jws.BT_IEXPLORER ];
		lVersion = lUA.match( /MSIE.*/i );
		if ( lVersion ) {
			lStr = lVersion[ 0 ].substr( 5 );
			p = lStr.indexOf( ";" );
			jws.fBrowserVerStr = p > 0 ? lStr.substr( 0, p ) : lStr;
			jws.fBrowserVerNo = parseFloat( jws.fBrowserVerStr );
		}
	} else if( jws.fIsFirefox ) {
		jws.fBrowserName = jws.BROWSER_NAMES[ jws.BT_FIREFOX ];
		//:i:de:Beispiel f&uuml;r userAgent bei FF 2.0.0.11:
		//:i:de:"Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11"
		lVersion = lUA.match( /Firefox\/.*/i );
		if ( lVersion ) {
			lStr = lVersion[ 0 ].substr( 8 );
			p = lStr.indexOf( " " );
			if( p > 0 ) {
				jws.fBrowserVerStr = lStr.substring( 0, p );
			} else	{
				jws.fBrowserVerStr = lStr;
			}	
			lFound = 0;
			i = 0;
			while( i < lStr.length ) {
				if( lStr.charAt( i ) == '.' ) {
					lFound++;
				}	
				if( lFound >= 2 ) {
					break;
				}	
				i++;
			}
			lStr = lStr.substring( 0, i );
			jws.fBrowserVerNo = parseFloat( lStr );
		}
	}
	else if( jws.fIsNetscape ) {
		jws.fBrowserName = jws.BROWSER_NAMES[ jws.BT_NETSCAPE ];
		//:i:de:Beispiel f&uuml;r userAgent bei FF 2.0.0.11:
		//:i:de:"Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11"
		lVersion = lUA.match( /Netscape\/.*/i );
		if ( lVersion ) {
			lStr = lVersion[ 0 ].substr( 9 );
			p = lStr.indexOf( " " );
			if( p > 0 ) {
				jws.fBrowserVerStr = lStr.substring( 0, p );
			} else {
				jws.fBrowserVerStr = lStr;
			}
			lFound = 0;
			i = 0;
			while( i < lStr.length ) {
				if( lStr.charAt( i ) == '.' ) {
					lFound++;
				}
				if( lFound >= 2 ) {
					break;
				}	
				i++;
			}
			lStr = lStr.substring( 0, i );
			jws.fBrowserVerNo = parseFloat( lStr );
		}
	} else if( jws.fIsOpera ) {
		//:i:de:Beispiel f&uuml;r userAgent bei Opera 9.24
		//:i:de:Opera/9.24 (Windows NT 5.1; U; en)
		jws.fBrowserName = jws.BROWSER_NAMES[ jws.BT_OPERA ];
		lVersion = lUA.match( /Opera\/.*/i );
		if ( lVersion ) {
			lStr = lVersion[ 0 ].substr( 6 );
			p = lStr.indexOf( " " );
			jws.fBrowserVerStr = p > 0 ? lStr.substr( 0, p ) : lStr;
			jws.fBrowserVerNo = parseFloat( lStr );
			// since 10.0 opera provides a separate "version" field
			lVersion = lUA.match( /Version\/.*/i );
			lStr = lVersion[ 0 ].substr( 8 );
			if ( lVersion ) {
				p = lStr.indexOf( " " );
				jws.fBrowserVerStr = ( p > 0 ? lStr.substr( 0, p ) : lStr ) + "/" + jws.fBrowserVerStr;
				jws.fBrowserVerNo = parseFloat( lStr );
			}
		}
	} else if( jws.fIsChrome ) {
		//:i:de:Beispiel f&uuml;r userAgent bei Chrome 4.0.211.7
		//:i:de:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.7 Safari/532.0
		jws.fBrowserName = jws.BROWSER_NAMES[ jws.BT_CHROME ];
		lVersion = lUA.match( /Chrome\/.*/i );
		if ( lVersion ) {
			lStr = lVersion[ 0 ].substr( 7 );
			p = lStr.indexOf( " " );
			jws.fBrowserVerStr = p > 0 ? lStr.substr( 0, p ) : lStr;
			jws.fBrowserVerNo = parseFloat( lStr );
		}
	} else if( jws.fIsSafari ) {
		jws.fBrowserName = jws.BROWSER_NAMES[ jws.BT_SAFARI ];
		//:i:de:Beispiel f&uuml;r userAgent bei Safari 3.0.4 (523.15):
		//:i:de:"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/523.15 (KHTML, like Gecko) Version/3.0 Safari/523.15"
		lVersion = lUA.match( /Version\/.*/i );
		if ( lVersion ) {
			lStr = lVersion[ 0 ].substr( 8 );
			p = lStr.indexOf( " " );
			jws.fBrowserVerStr = p > 0 ? lStr.substr( 0, p ) : lStr;

			lFound = 0;
			i = 0;
			while( i < lStr.length ) {
				if( lStr.charAt( i ) == '.' ) {
					lFound++;
				}	
				if( lFound >= 2 ) {
					break;
				}	
				i++;
			}
			lStr = lStr.substring( 0, i );
			jws.fBrowserVerNo = parseFloat( lStr );

			lVersion = lUA.match( /Safari\/.*/i );
			if ( lVersion ) {
				lStr = "." + lVersion[ 0 ].substr( 7 );
				p = lStr.indexOf( " " );
				jws.fBrowserVerStr += p > 0 ? lStr.substr( 0, p ) : lStr; 
			}
		}	
	}
}());
	

//:package:*:jws.events
//:class:*:jws.events
//:ancestor:*:-
//:d:en:Implements event abstraction for Internet Explorer.
jws.events = {

	//:m:*:addEventListener
	//:d:en:Adds a listener (callback) to an event in a cross-browser compatible way.
	//:a:en::aElement:Node:Source element that fires events.
	//:a:en::aEvent:String:Name of the event as a string.
	//:a:en::aListener:Function:The listener function which is called in case of the event.
	//:r:*:::void:none
	addEventListener : (
		jws.isIE ?
			function( aElement, aEvent, aListener ) {
				aElement.attachEvent( "on" + aEvent, aListener);
			}
		:
			function( aElement, aEvent, aListener ) {
				aElement.addEventListener( aEvent, aListener, false );
			}
	),

	// :d:en:Removes a listener (callback) from an event in a cross-browser compatible way.
	// :a:en::aElement:Node:Source element that fires events.
	// :a:en::aEvent:String:Name of the event as a string.
	// :a:en::aListener:Function:The listener function which is called in case of the event.

	//:m:*:getTarget
	//:d:en:Returns the element which originally fired the event in a cross-browser compatible way.
	//:r:*:::Node:Element that originally fired the event.
	getTarget : (
		jws.isIE ?
			function( aEvent ) {
				return aEvent.srcElement;
			}
		:
			function( aEvent ) {
				return aEvent.target;
			}
	),
	
	preventDefault : (
		jws.isIE ?
			function( aEvent ) {
				aEvent = window.event;
				if( aEvent ) {
					aEvent.returnValue = false;
				}
			}
		:
			function( aEvent ) {
				return aEvent.preventDefault();
			}
	)

};

//  <JasobNoObfs>
/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 * For full source please refer to: md5.js
 */
var hexcase=0;var b64pad="";function hex_md5(s){return rstr2hex(rstr_md5(str2rstr_utf8(s)));};function b64_md5(s){return rstr2b64(rstr_md5(str2rstr_utf8(s)));};function any_md5(s,e){return rstr2any(rstr_md5(str2rstr_utf8(s)),e);};function hex_hmac_md5(k,d){return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k),str2rstr_utf8(d)));};function b64_hmac_md5(k,d){return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k),str2rstr_utf8(d)));};function any_hmac_md5(k,d,e){return rstr2any(rstr_hmac_md5(str2rstr_utf8(k),str2rstr_utf8(d)),e);};function md5_vm_test(){return hex_md5("abc").toLowerCase()=="900150983cd24fb0d6963f7d28e17f72";};function rstr_md5(s){return binl2rstr(binl_md5(rstr2binl(s),s.length*8));};function rstr_hmac_md5(key,data){var bkey=rstr2binl(key);if(bkey.length>16)bkey=binl_md5(bkey,key.length*8);var ipad=Array(16),opad=Array(16);for(var i=0;i<16;i++){ipad[i]=bkey[i]^0x36363636;opad[i]=bkey[i]^0x5C5C5C5C;}var hash=binl_md5(ipad.concat(rstr2binl(data)),512+data.length*8);return binl2rstr(binl_md5(opad.concat(hash),512+128));};function rstr2hex(input){try{hexcase}catch(e){hexcase=0;}var hex_tab=hexcase?"0123456789ABCDEF":"0123456789abcdef";var output="";var x;for(var i=0;i<input.length;i++){x=input.charCodeAt(i);output+=hex_tab.charAt((x>>>4)&0x0F)+hex_tab.charAt(x&0x0F);}return output;};function rstr2b64(input){try{b64pad}catch(e){b64pad='';}var tab="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var output="";var len=input.length;for(var i=0;i<len;i+=3){var triplet=(input.charCodeAt(i)<<16)|(i+1<len?input.charCodeAt(i+1)<<8:0)|(i+2<len?input.charCodeAt(i+2):0);for(var j=0;j<4;j++){if(i*8+j*6>input.length*8)output+=b64pad;else output+=tab.charAt((triplet>>>6*(3-j))&0x3F);}}return output;};function rstr2any(input,encoding){var divisor=encoding.length;var i,j,q,x,quotient;var dividend=Array(Math.ceil(input.length/2));for(i=0;i<dividend.length;i++){dividend[i]=(input.charCodeAt(i*2)<<8)|input.charCodeAt(i*2+1);}var full_length=Math.ceil(input.length*8/(Math.log(encoding.length)/Math.log(2)));var remainders=Array(full_length);for(j=0;j<full_length;j++){quotient=Array();x=0;for(i=0;i<dividend.length;i++){x=(x<<16)+dividend[i];q=Math.floor(x/divisor);x-=q*divisor;if(quotient.length>0||q>0)quotient[quotient.length]=q;}remainders[j]=x;dividend=quotient;}var output="";for(i=remainders.length-1;i>=0;i--)output+=encoding.charAt(remainders[i]);return output;};function str2rstr_utf8(input){var output="";var i= -1;var x,y;while(++i<input.length){x=input.charCodeAt(i);y=i+1<input.length?input.charCodeAt(i+1):0;if(0xD800<=x&&x<=0xDBFF&&0xDC00<=y&&y<=0xDFFF){x=0x10000+((x&0x03FF)<<10)+(y&0x03FF);i++;}if(x<=0x7F)output+=String.fromCharCode(x);else if(x<=0x7FF)output+=String.fromCharCode(0xC0|((x>>>6)&0x1F),0x80|(x&0x3F));else if(x<=0xFFFF)output+=String.fromCharCode(0xE0|((x>>>12)&0x0F),0x80|((x>>>6)&0x3F),0x80|(x&0x3F));else if(x<=0x1FFFFF)output+=String.fromCharCode(0xF0|((x>>>18)&0x07),0x80|((x>>>12)&0x3F),0x80|((x>>>6)&0x3F),0x80|(x&0x3F));}return output;};function str2rstr_utf16le(input){var output="";for(var i=0;i<input.length;i++)output+=String.fromCharCode(input.charCodeAt(i)&0xFF,(input.charCodeAt(i)>>>8)&0xFF);return output;};function str2rstr_utf16be(input){var output="";for(var i=0;i<input.length;i++)output+=String.fromCharCode((input.charCodeAt(i)>>>8)&0xFF,input.charCodeAt(i)&0xFF);return output;};function rstr2binl(input){var output=Array(input.length>>2);for(var i=0;i<output.length;i++)output[i]=0;for(var i=0;i<input.length*8;i+=8)output[i>>5]|=(input.charCodeAt(i/8)&0xFF)<<(i%32);return output;};function binl2rstr(input){var output="";for(var i=0;i<input.length*32;i+=8)output+=String.fromCharCode((input[i>>5]>>>(i%32))&0xFF);return output;};function binl_md5(x,len){x[len>>5]|=0x80<<((len)%32);x[(((len+64)>>>9)<<4)+14]=len;var a=1732584193;var b= -271733879;var c= -1732584194;var d=271733878;for(var i=0;i<x.length;i+=16){var olda=a;var oldb=b;var oldc=c;var oldd=d;a=md5_ff(a,b,c,d,x[i+0],7,-680876936);d=md5_ff(d,a,b,c,x[i+1],12,-389564586);c=md5_ff(c,d,a,b,x[i+2],17,606105819);b=md5_ff(b,c,d,a,x[i+3],22,-1044525330);a=md5_ff(a,b,c,d,x[i+4],7,-176418897);d=md5_ff(d,a,b,c,x[i+5],12,1200080426);c=md5_ff(c,d,a,b,x[i+6],17,-1473231341);b=md5_ff(b,c,d,a,x[i+7],22,-45705983);a=md5_ff(a,b,c,d,x[i+8],7,1770035416);d=md5_ff(d,a,b,c,x[i+9],12,-1958414417);c=md5_ff(c,d,a,b,x[i+10],17,-42063);b=md5_ff(b,c,d,a,x[i+11],22,-1990404162);a=md5_ff(a,b,c,d,x[i+12],7,1804603682);d=md5_ff(d,a,b,c,x[i+13],12,-40341101);c=md5_ff(c,d,a,b,x[i+14],17,-1502002290);b=md5_ff(b,c,d,a,x[i+15],22,1236535329);a=md5_gg(a,b,c,d,x[i+1],5,-165796510);d=md5_gg(d,a,b,c,x[i+6],9,-1069501632);c=md5_gg(c,d,a,b,x[i+11],14,643717713);b=md5_gg(b,c,d,a,x[i+0],20,-373897302);a=md5_gg(a,b,c,d,x[i+5],5,-701558691);d=md5_gg(d,a,b,c,x[i+10],9,38016083);c=md5_gg(c,d,a,b,x[i+15],14,-660478335);b=md5_gg(b,c,d,a,x[i+4],20,-405537848);a=md5_gg(a,b,c,d,x[i+9],5,568446438);d=md5_gg(d,a,b,c,x[i+14],9,-1019803690);c=md5_gg(c,d,a,b,x[i+3],14,-187363961);b=md5_gg(b,c,d,a,x[i+8],20,1163531501);a=md5_gg(a,b,c,d,x[i+13],5,-1444681467);d=md5_gg(d,a,b,c,x[i+2],9,-51403784);c=md5_gg(c,d,a,b,x[i+7],14,1735328473);b=md5_gg(b,c,d,a,x[i+12],20,-1926607734);a=md5_hh(a,b,c,d,x[i+5],4,-378558);d=md5_hh(d,a,b,c,x[i+8],11,-2022574463);c=md5_hh(c,d,a,b,x[i+11],16,1839030562);b=md5_hh(b,c,d,a,x[i+14],23,-35309556);a=md5_hh(a,b,c,d,x[i+1],4,-1530992060);d=md5_hh(d,a,b,c,x[i+4],11,1272893353);c=md5_hh(c,d,a,b,x[i+7],16,-155497632);b=md5_hh(b,c,d,a,x[i+10],23,-1094730640);a=md5_hh(a,b,c,d,x[i+13],4,681279174);d=md5_hh(d,a,b,c,x[i+0],11,-358537222);c=md5_hh(c,d,a,b,x[i+3],16,-722521979);b=md5_hh(b,c,d,a,x[i+6],23,76029189);a=md5_hh(a,b,c,d,x[i+9],4,-640364487);d=md5_hh(d,a,b,c,x[i+12],11,-421815835);c=md5_hh(c,d,a,b,x[i+15],16,530742520);b=md5_hh(b,c,d,a,x[i+2],23,-995338651);a=md5_ii(a,b,c,d,x[i+0],6,-198630844);d=md5_ii(d,a,b,c,x[i+7],10,1126891415);c=md5_ii(c,d,a,b,x[i+14],15,-1416354905);b=md5_ii(b,c,d,a,x[i+5],21,-57434055);a=md5_ii(a,b,c,d,x[i+12],6,1700485571);d=md5_ii(d,a,b,c,x[i+3],10,-1894986606);c=md5_ii(c,d,a,b,x[i+10],15,-1051523);b=md5_ii(b,c,d,a,x[i+1],21,-2054922799);a=md5_ii(a,b,c,d,x[i+8],6,1873313359);d=md5_ii(d,a,b,c,x[i+15],10,-30611744);c=md5_ii(c,d,a,b,x[i+6],15,-1560198380);b=md5_ii(b,c,d,a,x[i+13],21,1309151649);a=md5_ii(a,b,c,d,x[i+4],6,-145523070);d=md5_ii(d,a,b,c,x[i+11],10,-1120210379);c=md5_ii(c,d,a,b,x[i+2],15,718787259);b=md5_ii(b,c,d,a,x[i+9],21,-343485551);a=safe_add(a,olda);b=safe_add(b,oldb);c=safe_add(c,oldc);d=safe_add(d,oldd);}return Array(a,b,c,d);};function md5_cmn(q,a,b,x,s,t){return safe_add(bit_rol(safe_add(safe_add(a,q),safe_add(x,t)),s),b);};function md5_ff(a,b,c,d,x,s,t){return md5_cmn((b&c)|((~b)&d),a,b,x,s,t);};function md5_gg(a,b,c,d,x,s,t){return md5_cmn((b&d)|(c&(~d)),a,b,x,s,t);};function md5_hh(a,b,c,d,x,s,t){return md5_cmn(b^c^d,a,b,x,s,t);};function md5_ii(a,b,c,d,x,s,t){return md5_cmn(c^(b|(~d)),a,b,x,s,t);};function safe_add(x,y){var lsw=(x&0xFFFF)+(y&0xFFFF);var msw=(x>>16)+(y>>16)+(lsw>>16);return(msw<<16)|(lsw&0xFFFF);};function bit_rol(num,cnt){return(num<<cnt)|(num>>>(32-cnt));}
//  </JasobNoObfs>


//:package:*:jws.tools
//:class:*:jws.tools
//:ancestor:*:-
//:d:en:Implements some required JavaScript tools.
jws.tools = {

	//:m:*:zerofill
	//:d:en:Fills up an integer value with the given number of zero characters
	//:d:en:to support a date time exchange according to ISO 8601
	//:a:en::aInt:Number:Number to be formatted.
	//:a:en::aDigits:Number:Nu,ber of digits for the result.
	//:r:*:::String:String with the exact number of digits filled with 0.
	zerofill: function( aInt, aDigits ) {
		var lRes = aInt.toFixed( 0 );
		if( lRes.length > aDigits ) {
			lRes = lRes.substring( lRes.length - aDigits );
		} else {
			while( lRes.length < aDigits ) {
				lRes = "0" + lRes;
			}
		}
        return lRes;
    },
	
	calcMD5: function( aUTF8 ) {
		return( hex_md5( aUTF8 ) );
	},
	
	escapeSQL: function( aValue ) {
		return aValue;
		/*
		if( aValue && typeof aValue == "string" ) {
			// escape single quotes in strings by double single quotes
			return aValue.replace( /[']/g, "''" );
		} else {
			return aValue;
		}
		*/
	},

	date2ISO: function( aDate ) {
		// JavaScript returns negative values for +GMT
		var lTZO = -aDate.getTimezoneOffset();
		var lAbsTZO = Math.abs( lTZO );
		var lRes =
			aDate.getUTCFullYear() 
			+ "-"
			+ this.zerofill( aDate.getUTCMonth() + 1, 2 )
			+ "-"
			+ this.zerofill( aDate.getUTCDate(), 2 )
			// use time separator
			+ "T"
			+ this.zerofill( aDate.getUTCHours(), 2 )
			+ ":"
			+ this.zerofill( aDate.getUTCMinutes(), 2 )
			+ ":"
			+ this.zerofill( aDate.getUTCSeconds(), 2 )
			+ "."
			+ this.zerofill( aDate.getUTCMilliseconds(), 3 )
			/*
			+ ( lTZO >= 0 ? "+" : "-" )
			+ this.zerofill( lAbsTZO / 60, 2 )
			+ this.zerofill( lAbsTZO % 60, 2 )
			*/
			// trailing Z means it's UTC
			+ "Z";
		return lRes;
	},

	ISO2Date: function( aISO, aTimezone ) {
		var lDate = new Date();
		// date part
		lDate.setUTCFullYear( aISO.substr( 0, 4 ) );
		lDate.setUTCMonth( aISO.substr( 5, 2 ) - 1 );
		lDate.setUTCDate( aISO.substr( 8, 2 ) );
		// time
		lDate.setUTCHours( aISO.substr( 11, 2 ) );
		lDate.setUTCMinutes( aISO.substr( 14, 2 ) );
		lDate.setUTCSeconds( aISO.substr( 17, 2 ) );
		lDate.setUTCMilliseconds( aISO.substr( 20, 3 ) );
		//:TODO:en:Analyze timezone
		return lDate;
	},

	date2String: function( aDate ) {
		var lRes =
			aDate.getUTCFullYear() 
			+ this.zerofill( aDate.getUTCMonth() + 1, 2 )
			+ this.zerofill( aDate.getUTCDate(), 2 )
			+ this.zerofill( aDate.getUTCHours(), 2 )
			+ this.zerofill( aDate.getUTCMinutes(), 2 )
			+ this.zerofill( aDate.getUTCSeconds(), 2 )
			+ this.zerofill( aDate.getUTCMilliseconds(), 2 )
		return lRes;
	},

	string2Date: function( aISO ) {
		var lDate = new Date();
		// date part
		lDate.setUTCFullYear( aISO.substr( 0, 4 ) );
		lDate.setUTCMonth( aISO.substr( 4, 2 ) - 1 );
		lDate.setUTCDate( aISO.substr( 6, 2 ) );
		// time
		lDate.setUTCHours( aISO.substr( 8, 2 ) );
		lDate.setUTCMinutes( aISO.substr( 10, 2 ) );
		lDate.setUTCSeconds( aISO.substr( 12, 2 ) );
		lDate.setUTCMilliseconds( aISO.substr( 14, 3 ) );
		return lDate;
	},
	
	generateSharedUTID: function(aToken){
		var string = JSON.stringify(aToken);
		var chars = string.split('');
		chars.sort();
		return hex_md5("{" + chars.toString() + "}");
	},

	getType: function(aObject){
		var value = aObject;
		var t = typeof value;

		if ("number" == t){
			if((parseFloat(value) == parseInt(value))){
				t = "integer";
			} else {
				t = "double";
			}
		} else if (Object.prototype.toString.call(value) === "[object Array]") {
			t = "array";
		} else if (value === null) {
			t = "null";
		}
		return t;
	}

};

if( !jws.browserSupportsNativeWebSockets ) {
	//	<JasobNoObfs>
	// --- swfobject.js ---
	// SWFObject v2.2 <http://code.google.com/p/swfobject/> 
	// is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
	var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
	//	</JasobNoObfs>
	//	
	// check if appropriate flash version is installed
	if( swfobject.hasFlashPlayerVersion( "10.0.0" ) ) {
	
	    WEB_SOCKET_DEBUG = true;
		
		// init flash bridge
		// use function to not polute the namespace with identifiers
		// get all scripts on the page to find jWebSocket.js path
		(function() {
			var lScripts = document.getElementsByTagName( "script" );
			for( var lIdx = 0, lCnt = lScripts.length; lIdx < lCnt; lIdx++ ) {
				var lScript = lScripts[ lIdx ];
				var lPath = lScript.src;
				if( !lPath ) {
					lPath = lScript.getAttribute( "src" );
				}
				if( lPath ) {
					// check for all three versions of jWebSocket.js 
					var lPos = lPath.lastIndexOf( "jWebSocket_Bundle_min.js" );
					if( lPos < 0 ) {
						lPos = lPath.lastIndexOf( "jWebSocket_Bundle.js" );
					}
					if( lPos < 0 ) {
						lPos = lPath.lastIndexOf( "jWebSocket.js" );
					}
					if( lPos > 0 ) {
						window.WEB_SOCKET_SWF_LOCATION = 
							lPath.substr( 0, lPos ) + "flash-bridge/WebSocketMain.swf";
						jws.JWS_FLASHBRIDGE = window.WEB_SOCKET_SWF_LOCATION;
						break;
					}
				}
			}
		})();
		
		if( window.WEB_SOCKET_SWF_LOCATION ) {
			//	<JasobNoObfs>
			// --- web_socket.js (minified) ---
			// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
			// License: New BSD License
			// Reference: http://dev.w3.org/html5/websockets/
			// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
			// Full Sources codes provided in web_socket.js
	 		(function(){if(window.WebSocket)return;var console=window.console;if(!console|| !console.log|| !console.error){console={log:function(){},error:function(){}};}if(!swfobject.hasFlashPlayerVersion("10.0.0")){console.error("Flash Player >= 10.0.0 is required.");return;}if(location.protocol=="file:"){console.error("WARNING: web-socket-js doesn't work in file:///... URL "+"unless you set Flash Security Settings properly. "+"Open the page via Web server i.e. http://...");}WebSocket=function(url,protocol,proxyHost,proxyPort,headers){var self=this;self.__id=WebSocket.__nextId++;WebSocket.__instances[self.__id]=self;self.readyState=WebSocket.CONNECTING;self.bufferedAmount=0;self.__events={};setTimeout(function(){WebSocket.__addTask(function(){WebSocket.__flash.create(self.__id,url,protocol,proxyHost||null,proxyPort||0,headers||null);});},0);};WebSocket.prototype.send=function(data){if(this.readyState==WebSocket.CONNECTING){throw "INVALID_STATE_ERR: Web Socket connection has not been established";}var result=WebSocket.__flash.send(this.__id,encodeURIComponent(data));if(result<0){return true;}else{this.bufferedAmount+=result;return false;}};WebSocket.prototype.close=function(){if(this.readyState==WebSocket.CLOSED||this.readyState==WebSocket.CLOSING){return;}this.readyState=WebSocket.CLOSING;WebSocket.__flash.close(this.__id);};WebSocket.prototype.addEventListener=function(type,listener,useCapture){if(!(type in this.__events)){this.__events[type]=[];}this.__events[type].push(listener);};WebSocket.prototype.removeEventListener=function(type,listener,useCapture){if(!(type in this.__events))return;var events=this.__events[type];for(var i=events.length-1;i>=0;--i){if(events[i]===listener){events.splice(i,1);break;}}};WebSocket.prototype.dispatchEvent=function(event){var events=this.__events[event.type]||[];for(var i=0;i<events.length;++i){events[i](event);}var handler=this["on"+event.type];if(handler)handler(event);};WebSocket.prototype.__handleEvent=function(flashEvent){if("readyState"in flashEvent){this.readyState=flashEvent.readyState;}var jsEvent;if(flashEvent.type=="open"||flashEvent.type=="error"){jsEvent=this.__createSimpleEvent(flashEvent.type);}else if(flashEvent.type=="close"){jsEvent=this.__createSimpleEvent("close");}else if(flashEvent.type=="message"){var data=decodeURIComponent(flashEvent.message);jsEvent=this.__createMessageEvent("message",data);}else{throw "unknown event type: "+flashEvent.type;}this.dispatchEvent(jsEvent);};WebSocket.prototype.__createSimpleEvent=function(type){if(document.createEvent&&window.Event){var event=document.createEvent("Event");event.initEvent(type,false,false);return event;}else{return{type:type,bubbles:false,cancelable:false};}};WebSocket.prototype.__createMessageEvent=function(type,data){if(document.createEvent&&window.MessageEvent&& !window.opera){var event=document.createEvent("MessageEvent");event.initMessageEvent("message",false,false,data,null,null,window,null);return event;}else{return{type:type,data:data,bubbles:false,cancelable:false};}};WebSocket.CONNECTING=0;WebSocket.OPEN=1;WebSocket.CLOSING=2;WebSocket.CLOSED=3;WebSocket.__flash=null;WebSocket.__instances={};WebSocket.__tasks=[];WebSocket.__nextId=0;WebSocket.loadFlashPolicyFile=function(url){WebSocket.__addTask(function(){WebSocket.__flash.loadManualPolicyFile(url);});};WebSocket.__initialize=function(){if(WebSocket.__flash)return;if(WebSocket.__swfLocation){window.WEB_SOCKET_SWF_LOCATION=WebSocket.__swfLocation;}if(!window.WEB_SOCKET_SWF_LOCATION){console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");return;}var container=document.createElement("div");container.id="webSocketContainer";container.style.position="absolute";if(WebSocket.__isFlashLite()){container.style.left="0px";container.style.top="0px";}else{container.style.left="-100px";container.style.top="-100px";}var holder=document.createElement("div");holder.id="webSocketFlash";container.appendChild(holder);document.body.appendChild(container);swfobject.embedSWF(WEB_SOCKET_SWF_LOCATION,"webSocketFlash","1","1","10.0.0",null,null,{hasPriority:true,swliveconnect:true,allowScriptAccess:"always"},null,function(e){if(!e.success){console.error("[WebSocket] swfobject.embedSWF failed");}});};WebSocket.__onFlashInitialized=function(){setTimeout(function(){WebSocket.__flash=document.getElementById("webSocketFlash");WebSocket.__flash.setCallerUrl(location.href);WebSocket.__flash.setDebug(! !window.WEB_SOCKET_DEBUG);for(var i=0;i<WebSocket.__tasks.length;++i){WebSocket.__tasks[i]();}WebSocket.__tasks=[];},0);};WebSocket.__onFlashEvent=function(){setTimeout(function(){try{var events=WebSocket.__flash.receiveEvents();for(var i=0;i<events.length;++i){WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);}}catch(e){console.error(e);}},0);return true;};WebSocket.__log=function(message){console.log(decodeURIComponent(message));};WebSocket.__error=function(message){console.error(decodeURIComponent(message));};WebSocket.__addTask=function(task){if(WebSocket.__flash){task();}else{WebSocket.__tasks.push(task);}};WebSocket.__isFlashLite=function(){if(!window.navigator|| !window.navigator.mimeTypes){return false;}var mimeType=window.navigator.mimeTypes["application/x-shockwave-flash"];if(!mimeType|| !mimeType.enabledPlugin|| !mimeType.enabledPlugin.filename){return false;}return mimeType.enabledPlugin.filename.match(/flashlite/i)?true:false;};if(!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION){if(window.addEventListener){window.addEventListener("load",function(){WebSocket.__initialize();},false);}else{window.attachEvent("onload",function(){WebSocket.__initialize();});}}})();
			//	</JasobNoObfs>
		}
		
	} else {
		// no Flash Player installed
		WebSocket = null;
	}
}


jws.XHR = {
	//:i:en:AJAX constants

	//:const:*:XHR_ASYNC
	//:d:de:Asynchrone Kommunikation mit dem Server verwenden. Laufender Prozess wird fortgesetzt.
	//:d:en:Use asynchronous communication with the server. The current process is continued.
	ASYNC: true,
	//:const:*:SYNC
	//:d:de:Synchrone Kommunikation mit dem Server verwenden. Laufender Prozess wird geblockt.
	//:d:en:Use synchronous communication with the server. The current process is blocked.
	SYNC: false,
	
	METHOD_GET: "get",
	METHOD_POST: "post",
	METHOD_HEAD: "head",
	
	getXHRInstance: function() {
		var lXHR = null;

		//:i:de:Firefox, Opera, Safari etc. verf&uuml;gen &uuml;ber ein XMLHttpRequest Objekt
		if ( window.XMLHttpRequest ) {
			lXHR = new XMLHttpRequest();
		//:i:de:f&uuml;r den Internet Explorer muss ein ActiveX Objekt instantiiert werden.
		} else if( window.ActiveXObject ) {
			/*
 var XMLHTTP_IDS = new Array('MSXML2.XMLHTTP.5.0',
                                     'MSXML2.XMLHTTP.4.0',
                                     'MSXML2.XMLHTTP.3.0',
                                     'MSXML2.XMLHTTP',
                                     'Microsoft.XMLHTTP' );
          var success = false;
          for (var i=0;i < XMLHTTP_IDS.length && !success; i++) {
              try {
                   xmlhttp = new ActiveXObject(XMLHTTP_IDS[i]);
                      success = true;
                } catch (e) {}
          }
          if (!success) {
              throw new Error('Unable to create XMLHttpRequest.');
          }
*/
			try {
				lXHR = new ActiveXObject( "Msxml2.XMLHTTP" );
			} catch( e1 ) {
				try{
					lXHR = new ActiveXObject( "Microsoft.XMLHTTP" );
				} catch( e2 ) {
					//:todo:de:Exception handling implementieren falls kein AJAX Object geladen werden kann!
					throw "f3.cfw.std.ex.xhr.NotAvail";
				}
			}
		} else {
			throw "f3.cfw.std.ex.xhr.NotAvail";
		}
		return lXHR;
	},

	isProtocolOk: function( aContext ) {
		if( !aContext ) {
			aContext = self;
		}	
		//:i:en:file protocol does not allow XHR requests.
		return(
			!(	aContext.location.protocol &&
				aContext.location.protocol.toLowerCase() == "file:"
				)
			);
	},
	
	//:i:de:Default AJAX handler, falls keine solchen von der Applikation bereit gestellt werden.
	//:i:en:default AJAX handler if no handler are provided by application
	mXHRSucClbk: function( aXHR, aArgs ) {
		throw "f3.cfw.std.ex.xhr.NoSuccObs";
	},

	mXHRErrClbk: function( aXHR, aArgs ) {
		throw "f3.cfw.std.ex.xhr.NoErrObs";
	},

	mXHRRespLsnr: function() {
		//:i:de:M&ouml;glicherweise kommt eine Antwort nachdem ein Fenster beendet wurde!
		var aOptions = arguments.callee.options;

		if( f3.ajax.Request !== undefined /* && f3.ajax.Request.mXHRStChgObs */ ) {

			if( aOptions.OnReadyStateChange ) {
				aOptions.OnReadyStateChange( aOptions.XHR, aOptions );
			}
			switch( aOptions.XHR.readyState ) {
				//:i:en:uninitialized
				case 0:
				//:i:en:loading
				case 1:
				//:i:en:loaded
				case 2:
				//:i:en:interactive
				case 3:
					break;
				//:i:en:complete
				case 4:
					clearTimeout( aOptions.hTimeout );
					if( aOptions.XHR.status == 200 ) {
						// aOptions.OnSuccess( aOptions.XHR, aOptions );
						f3.dom.Event.callObserver( aOptions.OnSuccess, aOptions.XHR, aOptions );
					} else	{
						// aOptions.OnError( aOptions.XHR, aOptions );
						f3.dom.Event.callObserver( aOptions.OnError, aOptions.XHR, aOptions );
					}	
					aOptions.XHR = null;
					aOptions = null;
					arguments.callee.self = null;
					arguments.callee.options = null;
					// arguments.callee = null;
					break;
				default:
					aOptions.OnError( aOptions.XHR, aOptions );
					aOptions.XHR = null;
					aOptions = null;
					arguments.callee.self = null;
					arguments.callee.options = null;
					// arguments.callee = null;
					break;
			}
		}
	},

//	this.mXHRRespLsnr.options = aOptions;
//	this.mXHRRespLsnr.self = this;


	//:m:*:request
	//:d:de:Diese Methode f&uuml;hrt den eigentlichen XHR-Request aus.
	//:a:de::aURL:String:Server URL zu einer Datei (Ressource) oder einem Servlet oder einem anderen Dienst.
	//:a:de:aOptions:method:String:Entweder "post" (Daten&uuml;bermittlung im Post-Body) oder "get" (&uuml;bermittlung in der URL).
	//:a:de:aOptions:asynchronous:Boolean:Bestimmt ob die Anfrage asynchron (non-blocking) oder synchron (blocking) durchgef&uuml;hrt werden soll.
	//:a:de:aOptions:OnSuccess:Function:Callback der bei erfolgreicher Anfrage ausgef&uuml;hrt werden soll.
	//:a:de:aOptions:OnError:Function:Callback der bei fehlerhafter Anfrage ausgef&uuml;hrt werden soll.
	//:d:en:This method performs a AJAX call. The call either can be asynchronous or synchronous.
	//:a:en::aURL:String:Server URL to access a resource or servlet.
	//:a:en:aOptions:method:String:Can be "post" or "get".
	//:a:en:aOptions:asynchronous:Boolean:Perform the request asynchronously (non-blocking) oder synchronously (blocking).
	//:a:en:aOptions:OnSuccess:Function:Callback for a successful request.
	//:a:en:aOptions:OnError:Function:Callback for a erroneous request.
	//:r:*:::void
	request: function( aURL, aOptions ) {

		//i:de:Einige Vorgabewerte pr&uuml;fen...
		//i:en:Check some default values
		aOptions = f3.core.OOP.getDefaultOptions( aOptions, {
			method			: "POST",
			asynchronous	: f3.ajax.Common.ASYNC,
			postBody		: null,
			timeout			: -1,
			// username		: null,
			// password		: null,
			OnSuccess		: f3.ajax.Request.mXHRSucClbk,
			OnError			: f3.ajax.Request.mXHRErrClbk,
			contentType		: "text/plain; charset=UTF-8", // "application/x-www-form-urlencoded"
			cacheControl	: "must-revalidate"
		});

		//:i:de:Beim file Protokoll ist kein XHR m&ouml;glich.
		if( !f3.ajax.Common.isProtocolOk() ) {
			throw new Error( 0, f3.localeManager.getCurLocStr( "f3.cfw.std.ex.xhr.file" ) );
		}

		aOptions.XHR = f3.ajax.Common.getXHRInstance();
		if( aOptions.XHR ) {

			//:i:de:&Ouml;ffnen des XHR Objektes und...
			aOptions.XHR.open( aOptions.method, aURL, aOptions.asynchronous );

			//:i:de:Sobald ein Request offen ist, k&ouml;nnen wir den ContentType auf plain/text setzen.
			if ( aOptions.method.toLowerCase() == "post" ) {
				aOptions.XHR.setRequestHeader(
					"Content-type",
					aOptions.contentType
					);
			}

			if( aOptions.cacheControl )
				aOptions.XHR.setRequestHeader( "Cache-Control", aOptions.cacheControl );

			//:i:de:Eventhandler setzen (callback Funktion zuweisen)
			//:i:de:Dies funktioniert nicht f&uuml;r den Firefox im synchronen Modus, die Callback Funktion wird _
			//:i:de:nicht aufgerufen. Daher muss in diesem Fall der Handler nach dem "send" explizit _
			//:i:de:aufgerufen werden.
		
			var lResponseHandler = new $f3.$XHRResponse( aOptions );

			if( !f3.browser.Browser.isFirefox() || aOptions.asynchronous ) {
				aOptions.XHR.onreadystatechange = lResponseHandler.mXHRRespLsnr;
			// function() {
			//:i:de:M&ouml;glicherweise kommt eine Antwort nachdem ein Fenster beendet wurde!
			//	if( f3.ajax.Request !== undefined && f3.ajax.Request.mXHRStChgObs )
			//		f3.ajax.Request.mXHRStChgObs( aOptions );
			//};
			} else {
				aOptions.XHR.onreadystatechange = null;
			}
			
			if( aOptions.timeout > 0 ) {
				aOptions.hTimeout = 
				setTimeout(
					function() {
						aOptions.XHR.abort();
						if( f3.browser.Browser.isFirefox() && !aOptions.asynchronous ) {
							lResponseHandler.handler( aOptions );
						}
					},
					aOptions.timeout
					);
			}	
			
			//:i:de:...absetzen des Requests, bei GET-Requests ist postBody "null"
			try {
				aOptions.XHR.send( aOptions.postBody );
			} catch( e ) {
				aOptions.OnError( aOptions.XHR, aOptions );
			}	
			//:i:de:Siehe oben bzgl. Firefox Work-Around
			if( f3.browser.Browser.isFirefox() && !aOptions.asynchronous ) {
				lResponseHandler.mXHRRespLsnr( aOptions );
			}
		// f3.ajax.Request.mXHRStChgObs( aOptions );
		}

		//:todo:de:Was passiert, wenn kein AJAX Object geladen werden konnte? Z.B. wegen Sicherheitseinstellungen... ?
		//:todo:de:Es k&ouml;nnte ein hilfreiches Ergebnis erzeugt werden, z.B. ob und wie der Request ausgef&uuml;hrt werden konnte.

		return aOptions.XHR;
	}

}


if( !jws.browserSupportsNativeJSON ) {
	// <JasobNoObfs>
	// Please refer to http://json.org/js
	if(!this.JSON){this.JSON={};}(function(){function f(n){return n<10?'0'+n:n;}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}if(typeof rep==='function'){value=rep.call(holder,key,value);}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}v=partial.length===0?'[]':gap?'[\n'+gap+partial.join(',\n'+gap)+'\n'+mind+']':'['+partial.join(',')+']';gap=mind;return v;}if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}return str('',{'':value});};}if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}return reviver.call(holder,key,value);}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}throw new SyntaxError('JSON.parse');};}}());
	// </JasobNoObfs>
}

//	<JasobNoObfs>
//	Base64 encode / decode
//  http://www.webtoolkit.info/
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(input){var output="";var chr1,chr2,chr3,enc1,enc2,enc3,enc4;var i=0;input=Base64._utf8_encode(input);while(i<input.length){chr1=input.charCodeAt(i++);chr2=input.charCodeAt(i++);chr3=input.charCodeAt(i++);enc1=chr1>>2;enc2=((chr1&3)<<4)|(chr2>>4);enc3=((chr2&15)<<2)|(chr3>>6);enc4=chr3&63;if(isNaN(chr2)){enc3=enc4=64;}else if(isNaN(chr3)){enc4=64;}output=output+this._keyStr.charAt(enc1)+this._keyStr.charAt(enc2)+this._keyStr.charAt(enc3)+this._keyStr.charAt(enc4);}return output;},decode:function(input){var output="";var chr1,chr2,chr3;var enc1,enc2,enc3,enc4;var i=0;input=input.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(i<input.length){enc1=this._keyStr.indexOf(input.charAt(i++));enc2=this._keyStr.indexOf(input.charAt(i++));enc3=this._keyStr.indexOf(input.charAt(i++));enc4=this._keyStr.indexOf(input.charAt(i++));chr1=(enc1<<2)|(enc2>>4);chr2=((enc2&15)<<4)|(enc3>>2);chr3=((enc3&3)<<6)|enc4;output=output+String.fromCharCode(chr1);if(enc3!=64){output=output+String.fromCharCode(chr2);}if(enc4!=64){output=output+String.fromCharCode(chr3);}}output=Base64._utf8_decode(output);return output;},_utf8_encode:function(string){string=string.replace(/\r\n/g,"\n");var utftext="";for(var n=0;n<string.length;n++){var c=string.charCodeAt(n);if(c<128){utftext+=String.fromCharCode(c);}else if((c>127)&&(c<2048)){utftext+=String.fromCharCode((c>>6)|192);utftext+=String.fromCharCode((c&63)|128);}else{utftext+=String.fromCharCode((c>>12)|224);utftext+=String.fromCharCode(((c>>6)&63)|128);utftext+=String.fromCharCode((c&63)|128);}}return utftext;},_utf8_decode:function(utftext){var string="";var i=0;var c=c1=c2=0;while(i<utftext.length){c=utftext.charCodeAt(i);if(c<128){string+=String.fromCharCode(c);i++;}else if((c>191)&&(c<224)){c2=utftext.charCodeAt(i+1);string+=String.fromCharCode(((c&31)<<6)|(c2&63));i+=2;}else{c2=utftext.charCodeAt(i+1);c3=utftext.charCodeAt(i+2);string+=String.fromCharCode(((c&15)<<12)|((c2&63)<<6)|(c3&63));i+=3;}}return string;}}
//	</JasobNoObfs>


//	---------------------------------------------------------------------------
//  jWebSocket - some convenience JavaScript OOP tools
//	---------------------------------------------------------------------------
jws.oop = {};

// implement simple class declaration to support multi-level inheritance
// and easy 'inherited' calls (super-calls) in JavaScript
jws.oop.declareClass = function( aNamespace, aClassname, aAncestor, aFields ) {
	
	var lNS = self[ aNamespace ];
	if( !lNS ) { 
		self[ aNamespace ] = {};
	}
	
	var lConstructor = function() {
		if( this.create ) {
			this.create.apply( this, arguments );
		}
	};
	
	// publish the new class in the given name space
	lNS[ aClassname ] = lConstructor;

	// move all fields from spec to new class' prototype
	var lField;
	for( lField in aFields ) {
		lConstructor.prototype[ lField ] = aFields[ lField ];
	}
	if( aAncestor != null ) {
		// every class maintains an array of its direct descendants
		if( !aAncestor.descendants ) {
			aAncestor.descendants = [];
		}
		aAncestor.descendants.push( lConstructor );
		for( lField in aAncestor.prototype ) {
			var lAncMthd = aAncestor.prototype[ lField ];
			if( typeof lAncMthd == "function" ) {
				if( lConstructor.prototype[ lField ] ) {
					lConstructor.prototype[ lField ].inherited = lAncMthd;
				} else {
					lConstructor.prototype[ lField ] = lAncMthd;
				}
				// every method gets a reference to its super class
				// to allow class to inherited method from such
				lConstructor.prototype[ lField ].superClass = aAncestor;
			}
		}
	}
};


// plug-in functionality to allow to add plug-ins into existing classes
jws.oop.addPlugIn = function( aClass, aPlugIn ) {

	// if the class has no plug-ins yet initialize array
	if( !aClass.fPlugIns ) {
		aClass.fPlugIns = [];
	}
	// add the plug-in to the class
	aClass.fPlugIns.push( aPlugIn );
	// clone all methods of the plug-in to the class
	for( var lField in aPlugIn ) {
		// don't overwrite existing methods of class with plug-in methods
		// ensure that native jWebSocket methods don't get overwritten!
		if( !aClass.prototype[ lField ] ) {
			aClass.prototype[ lField ] = aPlugIn[ lField ];
			// var lObj = aClass.prototype[ lField ];
		}
	}
	// if the class already has descendants recursively
	// clone the plug-in methods to these as well.
	if( aClass.descendants ) {
		for( var lIdx = 0, lCnt = aClass.descendants.length; lIdx < lCnt; lIdx ++ ) {
			jws.oop.addPlugIn( aClass.descendants[ lIdx ], aPlugIn );
		}
	}
};


//	---------------------------------------------------------------------------
//  jWebSocket - Base Client
//  This class does not handle exceptions or error, it throws exceptions,
//  which are handled by the descendant classes.
//	---------------------------------------------------------------------------

//:package:*:jws
//:class:*:jws.jWebSocketBaseClient
//:ancestor:*:-
//:d:en:Implementation of the [tt]jws.jWebSocketBaseClient[/tt] class. _
//:d:en:This class does not handle exceptions or error, it throws exceptions, _
//:d:en:which are (have to be) handled by the descendant classes.

jws.oop.declareClass( "jws", "jWebSocketBaseClient", null, {

	create: function( aOptions ) {
		// turn off connection reliability by default
		if( !this.fReliabilityOptions ) {
			this.fReliabilityOptions = jws.RO_OFF;
		}
	},
	
	//:m:*:processOpened
	//:d:en:Called when the WebSocket connection successfully was established. _
	//:d:en:Can to be overwritten in descendant classes to process _
	//:d:en:[tt]onopen[/tt] event in descendant classes.
	//:a:en::aEvent:Object:Pending...
	//:r:*:::void:none
	processOpened: function( aEvent ) {
		// method to be overwritten in descendant classes
	},

	//:m:*:processPacket
	//:d:en:Called when a data packet was received. _
	//:d:en:Can to be overwritten in descendant classes to process _
	//:d:en:[tt]onmessage[/tt] event in descendant classes.
	//:a:en::aEvent:Object:Pending...
	//:r:*:::void:none
	processPacket: function( aEvent ) {
		// method to be overwritten in descendant classes
	},

	//:m:*:processClosed
	//:d:en:Called when the WebSocket connection was closed. _
	//:d:en:Can to be overwritten in descendant classes to process _
	//:d:en:[tt]onclose[/tt] event in descendant classes.
	//:a:en::aEvent:Object:Pending...
	//:r:*:::void:none
	processClosed: function( aEvent ) {
		// method to be overwritten in descendant classes
	},

	//:m:*:open
	//:d:en:Tries to establish a connection the jWebSocket server.
	//:a:en::aURL:String:URL to the jWebSocket Server
	//:a:en::aOptions:Object:Optional arguments, see below...
	//:a:en:aOptions:OnOpen:function:Callback when connection was successfully established.
	//:r:*:::void:none
	open: function( aURL, aOptions ) {
		if( !aOptions ) {
			aOptions = {};
		}
		// if browser natively supports WebSockets...
		// otherwise flash bridge may have embedded WebSocket class
		if( self.WebSocket ) {

			// TODO: !this.fConn is not enough here! Check for readystate!
			// if connection not already established...
			if( !this.fConn ) {
				var lThis = this;
				var lValue = null;

				// check if subprotocol is given
				// if not use JSON as default
				var lSubProt = jws.WS_SUBPROT_JSON;
				if( aOptions.subProtocol ) {
					lSubProt = aOptions.subProtocol;
				}
				// check if connection reliability is desired
				if( aOptions.reliabilityOptions ) {
					this.fReliabilityOptions = aOptions.reliabilityOptions;
				}
				// turn off isExplicitClose flag
				// to allow optional reconnect
				if( this.fReliabilityOptions ) {
					this.fReliabilityOptions.isExplicitClose = false;
				}
				
				// maintain own status flag
				if( this.fStatus != jws.RECONNECTING ) {
					this.fStatus = jws.CONNECTING;
				}	
				
				if( aOptions.OnOpenTimeout
					&& typeof aOptions.OnOpenTimeout == "function"
					&& aOptions.openTimeout ) {
					this.fOpenTimeout = aOptions.openTimeout;
					this.hOpenTimeout = setTimeout( function() {
						// debugger;
						lThis.fOpenTimeout = null;
						var aToken = {};
						aOptions.OnOpenTimeout( aToken );
					}, this.fOpenTimeout );
				}

				// create a new web socket instance
				this.fConn = new WebSocket( aURL, lSubProt );
				// save URL and sub prot for optional re-connect
				this.fURL = aURL; 
				this.fSubProt = lSubProt;
				
				// assign the listeners to local functions (closure) to allow
				// to handle event before and after the application
				this.fConn.onopen = function( aEvent ) {
					if( lThis.hOpenTimeout ) {
						clearTimeout( lThis.hOpenTimeout );
						lThis.hOpenTimeout = null;
					}
					lThis.fStatus = jws.OPEN;
					lValue = lThis.processOpened( aEvent );
					// give application change to handle event
					if( aOptions.OnOpen ) {
						aOptions.OnOpen( aEvent, lValue, lThis );
					}
					// process outgoing queue
					lThis.processQueue();
				};

				this.fConn.onmessage = function( aEvent ) {
					lValue = lThis.processPacket( aEvent );
					// give application change to handle event first
					if( aOptions.OnMessage ) {
						aOptions.OnMessage( aEvent, lValue, lThis );
					}
				};

				this.fConn.onclose = function( aEvent ) {
					if( lThis.hOpenTimeout ) {
						clearTimeout( lThis.hOpenTimeout );
						lThis.hOpenTimeout = null;
					}
					lThis.fStatus = jws.CLOSED;
					// check if still disconnect timeout active and clear if needed
					if( lThis.hDisconnectTimeout ) {
						clearTimeout( lThis.hDisconnectTimeout );
						delete lThis.hDisconnectTimeout;
					}
					lValue = lThis.processClosed( aEvent );
					// give application chance to handle event
					if( aOptions.OnClose ) {
						aOptions.OnClose( aEvent, lValue, lThis );
					}
					lThis.fConn = null;
					
					// connection was closed, 
					// check if auto-reconnect was configured
					if( lThis.fReliabilityOptions 
						&& lThis.fReliabilityOptions.autoReconnect 
						&& !lThis.fReliabilityOptions.isExplicitClose ) {
						
						lThis.fStatus = jws.RECONNECTING;
						
						lThis.hReconnectDelayTimeout = setTimeout(
							function() {
								if( aOptions.OnReconnecting ) {
									aOptions.OnReconnecting( aEvent, lValue, lThis );
								}
								lThis.open( lThis.fURL, aOptions );
							},
							lThis.fReliabilityOptions.reconnectDelay
						);
					}
				};

			} else {
				throw new Error( "Already connected" );
			}
		} else {
			throw new Error( "WebSockets not supported by browser" );
		}
	},

	//:m:*:connect
	//:a:en::aURL:String:Please refer to [tt]open[/tt] method.
	//:a:en::aOptions:Object:Please refer to [tt]open[/tt] method.
	//:r:*:::void:none
	connect: function( aURL, aOptions ) {
		return this.open(aURL, aOptions );
	},

	//:m:*:processQueue
	//:d:en:Processes the token queue. _
	//:d:en:Tries to send out all tokens stored in the quere
	//:a:en::::-
	//:r:*:::void:none
	processQueue: function() {
		// is there a queue at all?
		if( this.fOutQueue ) {
			var lRes = this.checkConnected();
			if( lRes.code == 0 ) {
				var lItem;
				while( this.fOutQueue.length > 0 ) {
					// get first element of the queue
					lItem = this.fOutQueue.shift();
					// and send it to the server
					this.fConn.send( lItem.packet );
				}
			}
		}
		// if no queue exists nothing needs to be done here.
	},

	//:m:*:queuePacket
	//:d:en:Adds a new token to the send queue
	//:d:en:this method can also be executed, if no connection is established
	//:a:en::aToken:Object:Token to be queued to the jWebSocket server.
	//:a:en::aOptions:Object:Optional arguments as listed below...
	//:a:en:aOptions:OnResponse:Function:Reference to callback function, which is called when the response is received.
	//:r:*:::void:none
	queuePacket: function( aPacket, aOptions ) {
		if( !this.fOutQueue ) {
			this.fOutQueue = [];
		}
		this.fOutQueue.push({
			packet: aPacket,
			options: aOptions
		});
	},

	//:m:*:sendStream
	//:d:en:Sends a given string to the jWebSocket Server. The methods checks _
	//:d:en:if the connection is still up and throws an exception if not.
	//:a:en::aData:String:String to be send the jWebSocketServer
	//:r:*:::void:none
	sendStream: function( aData ) {
		// is client already connected
		if( this.isOpened() ) {
			try {
				this.fConn.send( aData );
			} catch( lEx ) {
				// this is never fired !
				// console.log( "Could not send!" );
			}
		} else {
			if( this.isWriteable() ) {
				this.queuePacket( aData, null );
			} else {
				// if not raise exception
				throw new Error( "Not connected" );
			}	
		}
	},

	//:m:*:abortReconnect
	//:d:en:Aborts a pending automatic re-connection, if such.
	//:a:en::::none
	//:r:*:::boolean:[tt]true[/tt], if re-connect was pending, [tt]false[/tt] if nothing to abort.
	abortReconnect: function() {
		// in case connection could be established 
		// reset the re-connect interval.
		if( this.hReconnectDelayTimeout ) {
			clearTimeout( this.hReconnectDelayTimeout );
			this.hReconnectDelayTimeout = null;
			return true;
		}	
		return false;
	},

	//:m:*:setAutoReconnect
	//:d:en:Specifies whether to automatically re-connect in case of _
	//:d:en:connection loss.
	//:a:en::aAutoReconnect:Boolean:[tt]true[/tt] if auto-reconnect is desired, otherwise [tt]false[/tt].
	//:r:*:::void:none
	setAutoReconnect: function( aAutoReconnect ) {
		if( aAutoReconnect && typeof( aLimit ) == "boolean" ) {
			this.fReliabilityOptions.autoReconnect = aAutoReconnect;
		} else {
			this.fReliabilityOptions.autoReconnect = false;
		}
		// if no auto-reconnect is desired, abort a pending re-connect, if such.
		if( !( this.fReliabilityOptions && this.fReliabilityOptions.autoReconnect ) ) {
			abortReconnect();
		}
	},

	//:m:*:setQueueItemLimit
	//:d:en:Specifies the maximum number of allowed queue items. If a zero or _
	//:d:en:negative number is passed the number of items is not checked. _
	//:d:en:If the limit is exceeded the OnBufferOverflow event is fired.
	//:a:en::aLimit:Integer:Maximum of allowed messages in the queue.
	//:r:*:::void:none
	setQueueItemLimit: function( aLimit ) {
		if( aLimit && typeof( aLimit ) == "number" && aLimit > 0 ) {
			this.fReliabilityOptions.queueItemLimit = parseInt( aLimit );
		} else {
			this.fReliabilityOptions.queueItemLimit = 0;
		}
	},

	//:m:*:setQueueSizeLimit
	//:d:en:Specifies the maximum size in bytes allowed for the queue. If a zero or _
	//:d:en:negative number is passed the size of the queue is not checked. _
	//:d:en:If the limit is exceeded the OnBufferOverflow event is fired.
	//:a:en::aLimit:Integer:Maximum size of the queue in bytes.
	//:r:*:::void:none
	setQueueSizeLimit: function( aLimit ) {
		if( aLimit && typeof( aLimit ) == "number" && aLimit > 0 ) {
			this.fReliabilityOptions.queueSizeLimit = parseInt( aLimit );
		} else {
			this.fReliabilityOptions.queueSizeLimit = 0;
		}
	},

	//:m:*:setReliabilityOptions
	//:d:en:Specifies how the connection is management (null = no management) is done.
	//:a:en::aOptions:Object:The various connection management options.
	//:r:*:::void:none
	setReliabilityOptions: function( aOptions ) {
		this.fReliabilityOptions = aOptions;
		// if no auto-reconnect is desired, abort a pending re-connect, if such.
		// if no auto-reconnect is desired, abort a pending re-connect, if such.
		if( this.fReliabilityOptions ) {
			if( this.fReliabilityOptions.autoReconnect ) {
				//:todo:en:here we could think about establishing the connection
				// but this would required to pass all args for open!
			} else {
				abortReconnect();
			}	
		}
	},

	//:m:*:getReliabilityOptions
	//:d:en:Returns how the connection is management (null = no management) is done.
	//:a:en::aOptions:Object:The various connection management options.
	//:r:*:::void:none
	getReliabilityOptions: function() {
		return this.fReliabilityOptions;
	},

	//:m:*:getOutQueue
	//:d:en:Returns the outgoing message queue.
	//:a:en::::none
	//:r:*:::Array:The outgoing message queue, if such, otherwise [tt]undefined[/tt] or [tt]null[/tt].
	getOutQueue: function() {
		return this.fOutQueue;
	},

	//:m:*:resetSendQueue
	//:d:en:resets the send queue by simply deleting the queue field _
	//:d:en:of the connection.
	//:a:en::::none
	//:r:*:::void:none
	resetSendQueue: function() {
		delete this.fOutQueue;
	},

	//:m:*:isOpened
	//:d:en:Returns [tt]true[/tt] if the WebSocket connection opened up, otherwise [tt]false[/tt].
	//:a:en::::none
	//:r:*:::boolean:[tt]true[/tt] if the WebSocket connection is up otherwise [tt]false[/tt].
	isOpened: function() {
		return(
			this.fConn != undefined
			&& this.fConn != null
			&& this.fConn.readyState == jws.OPEN
		);
	},

	//:m:*:getURL
	//:d:en:Returns the URL if the WebSocket connection opened up, otherwise [tt]null[/tt].
	//:a:en::::none
	//:r:*:::String:the URL if the WebSocket connection opened up, otherwise [tt]null[/tt].
	getURL: function() {
		return this.fURL;
		/*
		return(
			this.fConn != undefined
			&& this.fConn != null
			&& this.fConn.readyState == jws.OPEN 
			? this.fURL
			: null
		);
		*/
	},

	//:m:*:getSubProt
	//:d:en:Returns the selected sub protocol when the WebSocket connection 
	//:d:en:was opened, otherwise [tt]null[/tt].
	//:a:en::::none
	//:r:*:::String:the URL if the WebSocket connection opened up, otherwise [tt]null[/tt].
	getSubProt: function() {
		return this.fSubProt;
	},
	
	//:m:*:isConnected
	//:@deprecated:en:Use [tt]isOpened()[/tt] instead.
	//:d:en:Returns [tt]true[/tt] if the WebSocket connection is up otherwise [tt]false[/tt].
	//:a:en::::none
	//:r:*:::boolean:[tt]true[/tt] if the WebSocket connection is up otherwise [tt]false[/tt].
	isConnected: function() {
		return( this.isOpened() );
	},

	//:m:*:forceClose
	//:d:en:Forces an immediate client side disconnect. The processClosed
	//:d:en:method is called if the connection was up otherwise no operation is
	//:d:en:performed.
	//:a:en::::none
	//:r:*:::void:none
	forceClose: function( aOptions ) {
		// if client closes usually no event is fired
		// here you optionally can fire it if required in your app!
		var lFireClose = false;
		// turn on isExplicitClose flag to not auto re-connect in case
		// of an explicit, i.e. desired client side close operation
		if( this.fReliabilityOptions ) {
			this.fReliabilityOptions.isExplicitClose = true;
		}
		if( aOptions ) {
			if( aOptions.fireClose && this.fConn.onclose ) {
				// TODO: Adjust to event fields 
				// if such are delivered in real event
				var lEvent = {};
				this.fConn.onclose( lEvent );
			}
		}
		if( this.fConn ) {
			// if( window.console ) { console.log( "forcing close...." ); }
			// reset listeners to prevent any kind of potential memory leaks.
			this.fConn.onopen = null;
			this.fConn.onmessage = null;
			this.fConn.onclose = null;
			// TODO: what about CONNECTING state ?!
			if( this.fConn.readyState == jws.OPEN ) {
				this.fConn.close();
			}
			// TODO: should be called only if client was really opened before
			this.processClosed();
		}
		// explicitely reset fConn to "null"
		this.fConn = null;
	},

	//:m:*:close
	//:d:en:Closes the connection either immediately or with an optional _
	//:d:en:timeout. _
	//:d:en:If the connection is established up an exception s fired.
	//:a:en::aOptions:Object:Optional arguments as listed below...
	//:a:en:aOptions:timeout:Number:The close timeout in milliseconds, default [tt]0[/tt].
	//:r:*:::void:none
	close: function( aOptions ) {
		// check if timeout option is used
		var lTimeout = 0;

		if( aOptions ) {
			if( aOptions.timeout ) {
				lTimeout = aOptions.timeout;
			}
		}
		// connection established at all?
		// TODO: Shouldn't we test for ready state here?
		if( this.fConn ) {
			if( lTimeout <= 0 ) {
				this.forceClose( aOptions );
			} else {
				var lThis = this;
				this.hDisconnectTimeout = setTimeout(
					function() {
						lThis.forceClose( aOptions );
					},
					lTimeout
				);
			}
		// throw exception if not connected
		} else {
			this.fConn = null;
			throw new Error( "Not connected" );
		}
	},

	//:m:*:disconnect
	//:d:en:Deprecated, kept for upward compatibility only. Do not use anymore! _
	//:d:en:Please refer to the [tt]close[/tt] method.
	//:a:en::aOptions:Object:Please refer to the [tt]close[/tt] method.
	//:r:*::::Please refer to the [tt]close[/tt] method.
	disconnect: function( aOptions ) {
		return this.close( aOptions );
	},

	addListener: function( aCallback ) {
		// if the class has no plug-ins yet initialize array
		if( !this.fListeners ) {
			this.fListeners = [];
		}
		this.fListeners.push( aCallback );
	},

	removeListener: function( aCallback ) {
		if( this.fListeners ) {
			for( var lIdx = 0, lCnt = this.fListeners; lIdx < lCnt; lIdx++ ) {
				if( aCallback == this.fListeners[ lIdx ] ) {
					this.fListeners.splice( lIdx, 1 );
				}
			}
		}
	},

	//:m:*:addPlugIn
	//:d:en:Adds a client side plug-in to the instance - not to the class!
	//:a:en::aPlugIn:Object:Plug-in to be appended to the client side plug-in chain.
	//:r:*:::void:none
	addPlugIn: function( aPlugIn, aId ) {
		// if the class has no plug-ins yet initialize array
		if( !this.fPlugIns ) {
			this.fPlugIns = [];
		}
		// add the plug-in to the class
		this.fPlugIns.push( aPlugIn );
/*
 		 var lField;
 */
		if( !aId ) {
			aId = aPlugIn.ID;
		}
		//:todo:en:check if plug-in with given id already exists!
		if( aId ) {
			aPlugIn.conn = this;
/*
			// blend all methods of the plug-in to the connection instance
			this[ aId ] = {
				conn: this
			};
			for( lField in aPlugIn ) {
				if( lField != "conn" ) {
					this[ aId ][ lField ] = aPlugIn[ lField ];
				}
			}
*/
		}
	},

	//:m:*:setParam
	//:d:en:Sets a certain parameter for the jWebSocket client instance. _
	//:d:en:Here you can add parameters you need for instance within events fired by this instance.
	//:d:en:To avoid name space conflicts you should prefer the setParamNS method.
	//:a:en::aKey:String:The name for the parameter.
	//:a:en::aValue:Object:The value for the parameter, can also be an object.
	//:r:*:::Object:Previous value of the parameter if such, otherwise null.
	setParam: function( aKey, aValue ) {
		if( !this.fParams ) {
			this.fParams = {};
		}
		var lOldValue = this.getParam( aKey );
		this.fParams[ aKey ] = aValue;
		return lOldValue;
	},

	//:m:*:getParam
	//:d:en:Gets the value for certain parameter of the jWebSocket client instance. _
	//:d:en:If no value is stored for the given parameter [tt]null[/tt] is returned.
	//:d:en:To avoid name space conflicts you should prefer the getParamNS method.
	//:a:en::aKey:String:The name for the parameter for which the value should be returned.
	//:r:*:::Object:Value of the parameter if such, otherwise null.
	getParam: function( aKey ) {
		if( !this.fParams ) {
			return null;
		}
		var lRes = this.fParams[ aKey ];
		if( lRes === undefined ) {
			return null;
		}
		return lRes;
	},

	//:m:*:setParamNS
	//:d:en:Sets a certain parameter for the jWebSocket client instance. _
	//:d:en:Here you can add parameters you need for instance within events fired by this instance.
	//:a:en::aNS:String:The name space for the parameter.
	//:a:en::aKey:String:The name for the parameter.
	//:a:en::aValue:Object:The value for the parameter, can also be an object.
	//:r:*:::Object:Previous value of the parameter if such, otherwise null.
	setParamNS: function( aNS, aKey, aValue ) {
		return this.setParam( aNS + "." + aKey, aValue );
	},

	//:m:*:getParam
	//:d:en:Gets the value for certain parameter of the jWebSocket client instance. _
	//:d:en:If no value is stored for the given parameter [tt]null[/tt] is returned.
	//:a:en::aNS:String:The name space for the parameter.
	//:a:en::aKey:String:The name for the parameter for which the value should be returned.
	//:r:*:::Object:Value of the parameter if such, otherwise null.
	getParamNS: function( aNS, aKey ) {
		return this.getParam( aNS + "." + aKey );
	},
	
	//:m:*:clearParams
	//:d:en:Resets all params of this jWebSocket client. _
	//:d:en:After this operation all params are removed and cannot be used anymore.
	//:a:en::::none
	//:r:*:::void:none
	clearParams: function() {
		if( this.fParams ) {
			delete this.fParams;
		}
	}

});


//	---------------------------------------------------------------------------
//  jWebSocket token client (this is an abstract class)
//  don't create direct instances of jWebSocketTokenClient
//	---------------------------------------------------------------------------

//:package:*:jws
//:class:*:jws.jWebSocketTokenClient
//:ancestor:*:jws.jWebSocketBaseClient
//:d:en:Implementation of the [tt]jWebSocketTokenClient[/tt] class. This is _
//:d:en:an abstract class as an ancestor for the JSON-, CSV- and XML client. _
//:d:en:Do not create direct instances of jWebSocketTokenClient.
jws.oop.declareClass( "jws", "jWebSocketTokenClient", jws.jWebSocketBaseClient, {

	//:m:*:create
	//:d:en:This method is called by the contructor of this class _
	//:d:en:to init the instance.
	//:a:en::::none
	//:r:*:::void:none
	create: function( aOptions ) {
		// call inherited create
		arguments.callee.inherited.call( this, aOptions );
		this.fRequestCallbacks = {};
	},

	//:m:*:getId
	//:d:en:Returns the unique id of this client assigned by the jWebSocket server.
	//:a:en::::none
	//:r:*:::String:Unique id of this client.
	getId: function() {
		return this.fClientId;
	},

	//:m:*:checkCallbacks
	//:d:en:Processes an incoming result token and assigns it to a previous _
	//:d:en:request. If a request was found it calls it OnResponse method _
	//:d:en:and removes the reference of the list of pending results.
	//:d:en:This method is used internally only and should not be called by _
	//:d:en:the application.
	//:a:en::aToken:Object:The incoming result token.
	//:r:*:::void:none
	checkCallbacks: function( aToken ) {
		var lField = "utid" + aToken.utid;
		// console.log( "checking result for utid: " + aToken.utid + "..." );
		var lClbkRec = this.fRequestCallbacks[ lField ];
		if( lClbkRec ) {
			// result came in within the given timeout
			// first cleanup timeout observer because
			// OnResponse listener potentially could take a while as well
			if( lClbkRec.hCleanUp ) {
				// thus reset the timeout observer
				clearTimeout( lClbkRec.hCleanUp );
			}
			var lArgs = lClbkRec.args;
			var lCallback = lClbkRec.callback;
			if( lCallback.OnResponse ) {
				lCallback.OnResponse.call( this, aToken, lArgs );
			}	
			if( lCallback.OnSuccess
				&& aToken.code === 0 ) {
				lCallback.OnSuccess.call( this, aToken, lArgs );
			}	
			if( lCallback.OnFailure
				&& aToken.code != undefined 
				&& aToken.code != 0 ) {
				lCallback.OnFailure.call( this, aToken, lArgs );
			}
			delete this.fRequestCallbacks[ lField ];
		}
	},

	//:m:*:createDefaultResult
	//:d:en:Creates a response token with [tt]code = 0[/tt] and _
	//:d:en:[tt]msg = "Ok"[/tt]. It automatically increases the TOKEN_ID _
	//:d:en:to obtain a unique serial id for the next request.
	//:a:en::::none
	//:r:*:::void:none
	createDefaultResult: function() {
		return{
			code: 0,
			msg: "Ok",
			localeKey: "jws.jsc.res.Ok",
			args: null,
			tid: jws.CUR_TOKEN_ID
		};
	},

	//:m:*:checkConnected
	//:d:en:Checks if the client is connected and if so returns a default _
	//:d:en:response token (please refer to [tt]createDefaultResult[/tt] _
	//:d:en:method. If the client is not connected an error token is returned _
	//:d:en:with [tt]code = -1[/tt] and [tt]msg = "Not connected"[/tt]. _
	//:d:en:This is a convenience method if a function needs to check if _
	//:d:en:the client is connected and return an error token if not.
	//:a:en::::none
	//:r:*:::void:none
	checkConnected: function() {
		var lRes = this.createDefaultResult();
		if( !this.isOpened() ) {
			lRes.code = -1;
			lRes.localeKey = "jws.jsc.res.notConnected";
			lRes.msg = "Not connected.";
		}
		return lRes;
	},

	//:m:*:isWriteable
	//:d:en:Checks if the client currently is able to process send commands. _
	//:d:en:In case the connection-reliability option in turned on the _
	//:d:en:write queue is used to buffer outgoing packets. The queue may be _
	//:d:en:in number of items as as well as in size and time.
	//:a:en::::none
	//:r:*:::void:none
	isWriteable: function() {
		return(
			this.isOpened() || this.fStatus == jws.RECONNECTING
		);
	},

	//:m:*:checkWriteable
	//:d:en:Checks if the client is connected and if so returns a default _
	//:d:en:response token (please refer to [tt]createDefaultResult[/tt] _
	//:d:en:method. If the client is not connected an error token is returned _
	//:d:en:with [tt]code = -1[/tt] and [tt]msg = "Not connected"[/tt]. _
	//:d:en:This is a convenience method if a function needs to check if _
	//:d:en:the client is connected and return an error token if not.
	//:a:en::::none
	//:r:*:::void:none
	checkWriteable: function() {
		var lRes = this.createDefaultResult();
		if( !this.isWriteable() ) {
			lRes.code = -1;
			lRes.localeKey = "jws.jsc.res.notWriteable";
			lRes.msg = "Not writable.";
		}
		return lRes;
	},

	//:m:*:checkLoggedIn
	//:d:en:Checks if the client is connected and logged in and if so returns _
	//:d:en:a default response token (please refer to [tt]createDefaultResult[/tt] _
	//:d:en:method. If the client is not connected or nott logged in an error _
	//:d:en:token is returned with [tt]code = -1[/tt] and _
	//:d:en:[tt]msg = "Not logged in"[/tt]. _
	//:d:en:This is a convenience method if a function needs to check if _
	//:d:en:the client is connected and return an error token if not.
	//:a:en::::none
	//:r:*:::void:none
	checkLoggedIn: function() {
		var lRes = this.checkConnected();
		if( lRes.code == 0 && !this.isLoggedIn() ) {
			lRes.code = -1;
			lRes.localeKey = "jws.jsc.res.notLoggedIn";
			lRes.msg = "Not logged in.";
		}
		return lRes;
	},

	//:m:*:resultToString
	//:d:en:Converts a result token to a readable string e.g. to be displayed _
	//:d:en:in the GUI.
	//:a:en::aResToken:Object:The result token to be converted into a string.
	//:r:*:::String:The human readable string output of the result token.
	resultToString: function( aResToken ) {
		return(
			( aResToken && typeof aResToken == "object" && aResToken.msg ? 
				aResToken.msg : "invalid response token" )
			// + " (code: " + aRes.code + ", tid: " + aRes.tid + ")"
		);
	},

	//:m:*:tokenToStream
	//:d:en:Converts a token into a string (stream). This method needs to be _
	//:d:en:overwritten by the descendant classes to implement a certain _
	//:d:en:sub protocol like JSON, CSV or XML. If you call this method _
	//:d:en:directly an exception is raised.
	//:a:en::aToken:Object:Token to be converted into a stream.
	//:r:*:::void:none
	tokenToStream: function( aToken ) {
		// this is supposed to convert a token into a string stream which is
		// send to the server, not implemented in base class.
		// needs to be overwritten in descendant classes!
		throw new Error( "tokenToStream needs to be overwritten in descendant classes" );
	},

	//:m:*:streamToToken
	//:d:en:Converts a string (stream) into a token. This method needs to be _
	//:d:en:overwritten by the descendant classes to implement a certain _
	//:d:en:sub protocol like JSON, CSV or XML. If you call this method _
	//:d:en:directly an exception is raised.
	//:a:en::aStream:String:Stream to be converted into a token.
	//:r:*:::void:none
	streamToToken: function( aStream ) {
		// this is supposed to convert a string stream from the server into 
		// a token (object), not implemented in base class.
		// needs to be overwritten in descendant classes
		throw new Error( "streamToToken needs to be overwritten in descendant classes" );
	},

	//:m:*:notifyPlugInsOpened
	//:d:en:Iterates through the client side plug-in chain and calls the _
	//:d:en:[tt]processOpened[/tt] method of each plug-in after the client _
	//:d:en:successfully established the connection to the server.
	//:d:en:By this mechanism all plug-ins easily can handle a new connection.
	//:a:en::::none
	//:r:*:::void:none
	notifyPlugInsOpened: function() {
		var lToken = {
			sourceId: this.fClientId
		};
		// notify all plug-ins about sconnect event
		var lPlugIns = jws.jWebSocketTokenClient.fPlugIns;
		if( lPlugIns ) {
			for( var lIdx = 0, lLen = lPlugIns.length; lIdx < lLen; lIdx++ ) {
				var lPlugIn = lPlugIns[ lIdx ];
				if( lPlugIn.processOpened ) {
					lPlugIn.processOpened.call( this, lToken );
				}
			}
		}
	},

	//:m:*:notifyPlugInsClosed
	//:d:en:Iterates through the client side plug-in chain and calls the _
	//:d:en:[tt]processClosed[/tt] method of each plug-in after the client _
	//:d:en:successfully established the connection to the server.
	//:d:en:By this mechanism all plug-ins easily can handle a terminated connection.
	//:a:en::::none
	//:r:*:::void:none
	notifyPlugInsClosed: function() {
		var lToken = {
			sourceId: this.fClientId
		};
		// notify all plug-ins about disconnect event
		var lPlugIns = jws.jWebSocketTokenClient.fPlugIns;
		if( lPlugIns ) {
			for( var lIdx = 0, lLen = lPlugIns.length; lIdx < lLen; lIdx++ ) {
				var lPlugIn = lPlugIns[ lIdx ];
				if( lPlugIn.processClosed ) {
					lPlugIn.processClosed.call( this, lToken );
				}
			}
		}
		// in case of a server side close event...
		this.fConn = null;
		// reset the session...
		this.fSessionId = null;
		// and the username as well
		this.fUsername = null;
	},

	//:m:*:processPacket
	//:d:en:Is called when a new raw data packet is received by the client. _
	//:d:en:This methods calls the [tt]streamToToken[/tt] method of the _
	//:d:en:its descendant who is responsible to implement the sub protocol _
	//:d:en:JSON, CSV or XML, here to parse the raw packet in the corresponding _
	//:d:en:format.
	//:a:en::aEvent:Object:Event object from the browser's WebSocket instance.
	//:r:*:::void:none
	processPacket: function( aEvent ) {
		// parse incoming token...
		var lToken = this.streamToToken( aEvent.data );
		// and process it...
		this.processToken( lToken );
		return lToken;
	},

	// TODO: move handlers to system plug-in in the same way as on server.
	// TODO: No change for application!
	//:m:*:processToken
	//:d:en:Processes an incoming token. The method iterates through all _
	//:d:en:plug-ins and calls their specific [tt]processToken[/tt] method.
	//:a:en::aToken:Object:Token to be processed by the plug-ins in the plug-in chain.
	//:r:*:::void:none
	processToken: function( aToken ) {

		// TODO: Remove this temporary hack with final release 1.0
		// TODO: this was required to ensure upward compatibility from 0.10 to 0.11
		var lNS = aToken.ns;
		if ( lNS != null && lNS.indexOf( "org.jWebSocket" ) == 1 ) {
			aToken.ns = "org.jwebsocket" + lNS.substring( 15 );
		} else if( lNS == null ) {
			aToken.ns = "org.jwebsocket.plugins.system";
		}

		// is it a token from the system plug-in at all?
		if( jws.NS_SYSTEM == aToken.ns ) {
			// check welcome and goodBye tokens to manage the session
			if( aToken.type == "welcome" && aToken.usid ) {
				this.fSessionId = aToken.usid;
				this.fClientId = aToken.sourceId;
				this.notifyPlugInsOpened();
				// fire OnWelcome Event if assigned
				if( this.fOnWelcome ) {
					this.fOnWelcome( aToken );
				}
				var lFlashBridgeVer = "n/a";
				if( swfobject) {
					var lInfo = swfobject.getFlashPlayerVersion();
					lFlashBridgeVer = lInfo.major + "." + lInfo.minor + "." + lInfo.release;
				}
				this.sendToken({
					ns: jws.SystemClientPlugIn.NS,
					type: "header",
					clientType: "browser",
					clientName: jws.getBrowserName(),
					clientVersion: jws.getBrowserVersionString(),
					clientInfo: navigator.userAgent,
					jwsType: "javascript",
					jwsVersion: jws.VERSION,
					jwsInfo: 
						jws.browserSupportsNativeWebSockets 
							? "native"
							: "flash " + lFlashBridgeVer
					}, {
					}
				);
			} else if( aToken.type == "goodBye" ) {
				// fire OnGoodBye Event if assigned
				if( this.fOnGoodBye ) {
					this.fOnGoodBye( aToken );
				}
				this.fSessionId = null;
				this.fUsername = null;
			} else if( aToken.type == "close" ) {
				// if the server closes the connection close immediately too.
				this.close({
					timeout: 0
				});
			// check if we got a response from a previous request
			} else if( aToken.type == "response" ) {
				// check login and logout manage the username
				if( aToken.reqType == "login" ) {
					this.fUsername = aToken.username;
					// if re-login used previous session-id re-assign it here!
					if( aToken.usid ) {
						this.fSessionId = aToken.usid;
					}
				}
				if( aToken.reqType == "logout" ) {
					this.fUsername = null;
				}
				// check if some requests need to be answered
				this.checkCallbacks( aToken );
			} else if( aToken.type == "event" ) {
				// check login and logout manage the username
				if( aToken.name == "connect" ) {
					this.processConnected( aToken );
				}
				if( aToken.name == "disconnect" ) {
					this.processDisconnected( aToken );
				}
			}
		} else {
			// check the incoming token for an optional response callback
			this.checkCallbacks( aToken );
		}

		var lIdx, lLen, lPlugIns, lPlugIn;

		// notify all plug-ins bound to the class
		// that a token has to be processed
		lPlugIns = jws.jWebSocketTokenClient.fPlugIns;
		if( lPlugIns ) {
			for( lIdx = 0, lLen = lPlugIns.length; lIdx < lLen; lIdx++ ) {
				lPlugIn = lPlugIns[ lIdx ];
				if( lPlugIn.processToken ) {
					lPlugIn.processToken.call( this, aToken );
				}
			}
		}

		// notify all plug-ins bound to the instance
		// that a token has to be processed
		lPlugIns = this.fPlugIns;
		if( lPlugIns ) {
			for( lIdx = 0, lLen = lPlugIns.length; lIdx < lLen; lIdx++ ) {
				lPlugIn = lPlugIns[ lIdx ];
				if( lPlugIn.processToken ) {
					lPlugIn.processToken( aToken );
				}
			}
		}

		// if the instance got an OnToken event assigned
		// fire the event
		if( this.fOnToken ) {
			this.fOnToken( aToken );
		}

		if( this.fListeners ) {
			for( lIdx = 0, lLen = this.fListeners.length; lIdx < lLen; lIdx++ ) {
				this.fListeners[ lIdx ]( aToken );
			}
		}

	},

	//:m:*:processClosed
	//:d:en:Iterates through all plug-ins of the plugin-chain and calls their _
	//:d:en:specific [tt]processClosed[/tt] method.
	//:a:en::aEvent:Object:...
	//:r:*:::void:none
	processClosed: function( aEvent ) {
		this.notifyPlugInsClosed();
		this.fClientId = null;
	},

	//:m:*:processConnected
	//:d:en:Called when the client successfully received a connect event token _
	//:d:en:which means that another client has connected to the network.
	//:a:en::aToken:Object:...
	//:r:*:::void:none
	processConnected: function( aToken ) {
		// notify all plug-ins that a new client connected
		var lPlugIns = jws.jWebSocketTokenClient.fPlugIns;
		if( lPlugIns ) {
			for( var lIdx = 0, lLen = lPlugIns.length; lIdx < lLen; lIdx++ ) {
				var lPlugIn = lPlugIns[ lIdx ];
				if( lPlugIn.processConnected ) {
					lPlugIn.processConnected.call( this, aToken );
				}
			}
		}
	},

	//:m:*:processDisconnected
	//:d:en:Called when the client successfully received a disconnect event token _
	//:d:en:which means that another client has disconnected from the network.
	//:a:en::aToken:Object:...
	//:r:*:::void:none
	processDisconnected: function( aToken ) {
		// notify all plug-ins that a client disconnected
		var lPlugIns = jws.jWebSocketTokenClient.fPlugIns;
		if( lPlugIns ) {
			for( var lIdx = 0, lLen = lPlugIns.length; lIdx < lLen; lIdx++ ) {
				var lPlugIn = lPlugIns[ lIdx ];
				if( lPlugIn.processDisconnected ) {
					lPlugIn.processDisconnected.call( this, aToken );
				}
			}
		}
	},

	//:m:*:sendToken
	//:d:en:Sends a token to the jWebSocket server.
	//:a:en::aToken:Object:Token to be send to the jWebSocket server.
	//:a:en::aOptions:Object:Optional arguments as listed below...
	//:a:en:aOptions:OnResponse:Function:Reference to callback function, which is called when the response is received.
	//:r:*:::void:none
	sendToken: function( aToken, aOptions ) {
		var lRes = this.checkWriteable();
		if( lRes.code == 0 ) {
			var lSpawnThread = false;
			var lL2FragmSize = 0;
			var lTimeout = jws.DEF_RESP_TIMEOUT;
			var lKeepRequest = false;
			var lArgs = null;
			var lCallbacks = {
				OnResponse: null,
				OnSuccess: null,
				OnFailure: null,
				OnTimeout: null
			};
			// we need to check for a response only
			// if correspondig callbacks are set
			var lControlResponse = false;
			if( aOptions ) {
				if( aOptions.OnResponse ) {
					lCallbacks.OnResponse = aOptions.OnResponse;
					lControlResponse = true;
				}
				if( aOptions.OnFailure) {
					lCallbacks.OnFailure = aOptions.OnFailure;
					lControlResponse = true;
				}
				if( aOptions.OnSuccess ) {
					lCallbacks.OnSuccess = aOptions.OnSuccess;
					lControlResponse = true;
				}
				if( aOptions.OnTimeout ) {
					lCallbacks.OnTimeout = aOptions.OnTimeout;
					lControlResponse = true;
				}
				if( aOptions.args ) {
					lArgs = aOptions.args;
				}
				if( aOptions.timeout ) {
					lTimeout = aOptions.timeout;
				}
				if( aOptions.spawnThread ) {
					lSpawnThread = aOptions.spawnThread;
				}
				if( aOptions.fragmentSize ) {
					lL2FragmSize = aOptions.fragmentSize;
				}
				if( aOptions.keepRequest ) {
					lKeepRequest = true;
				}
			}
			jws.CUR_TOKEN_ID++;
			if( lControlResponse ) {
				var lUTID = jws.CUR_TOKEN_ID;
				var lClbkId = "utid" + lUTID;
				var lThis = this;
				var lClbkRec = {
					request: new Date().getTime(),
					callback: lCallbacks,
					args: lArgs,
					timeout: lTimeout
				};
				if( lKeepRequest ) {
					lClbkRec.request = aToken;
				}
				this.fRequestCallbacks[ lClbkId ] = lClbkRec;
				// set timeout to observe response
				lClbkRec.hCleanUp = setTimeout( function() {
					var lCallbacks = lClbkRec.callback;
					// delete callback first to not fire response event
					// in case the OnTimeout processing takes longer or
					// even invokes blocking methods like alert.
					delete lThis.fRequestCallbacks[ lClbkId ];
					// now the OnTimeout Callback can be called.
					if( lCallbacks.OnTimeout ) {
						lCallbacks.OnTimeout.call( this, aToken, {
							utid: lUTID,
							timeout: lTimeout
						});
					}
				}, lTimeout );
			}
			if( lSpawnThread ) {
				aToken.spawnThread = true;
			}
			var lStream = this.tokenToStream( aToken );
			if( lL2FragmSize > 0 && lStream.length > 0 ) {
				var lToken, lFragment, lFragmId = 0, lStart = 0, lTotal = lStream.length;
				while( lStream.length > 0 ) {
					lToken = {
						ns: jws.NS_SYSTEM,
						type: "fragment",
						utid: aToken.utid,
						index: lFragmId++,
						total: parseInt( lTotal / lL2FragmSize ) + 1,
						data: lStream.substr( 0, lL2FragmSize )
					};
					lStart += lL2FragmSize;
					lStream = lStream.substr( lL2FragmSize );
					lFragment = this.tokenToStream( lToken );
					this.sendStream( lFragment );
					// console.log( "sending fragment " + lFragment + "..." );
				}
			} else {
				// console.log( "sending stream " + lStream + "..." );
				this.sendStream( lStream );
			}
		}
		return lRes;
	},

	//:m:*:getLastTokenId
	//:d:en:Returns the last token id that has been used for the last recent
	//:d:en:request.This id was already used and cannot be used for further
	//:d:en:tranmissions.
	//:a:en::::none
	//:r:*:::Integer:Last recently used unique token-id.
	getLastTokenId: function() {
		return jws.CUR_TOKEN_ID;
	},

	//:m:*:getNextTokenId
	//:d:en:Returns the next token id that will be used for the next request.
	//:d:en:This id will be used by the next sendToken call.
	//:a:en::::none
	//:r:*:::Integer:Next unique token-id used for the next sendToken call.
	getNextTokenId: function() {
		return jws.CUR_TOKEN_ID + 1;
	},

	//:m:*:sendText
	//:d:en:Sends a simple text message to a certain target client within the _
	//:d:en:WebSocket network by creating and sending a [tt]send[/tt] token. _
	//:d:en:The receiver must be addressed by its client id.
	//:d:en:This method requires the user to be authenticated.
	//:a:en::aTarget:String:Client id of the target client for the message.
	//:a:en::aText:String:Textmessage to be send to the target client.
	//:r:*:::void:none
	sendText: function( aTarget, aText ) {
		var lRes = this.checkLoggedIn();
		if( lRes.code == 0 ) {
			this.sendToken({
				ns: jws.NS_SYSTEM,
				type: "send",
				targetId: aTarget,
				sourceId: this.fClientId,
				sender: this.fUsername,
				data: aText
			});
		}
		return lRes;
	},

	//:m:*:broadcastText
	//:d:en:Broadcasts a simple text message to all clients or a limited set _
	//:d:en:of clients within the WebSocket network by creating and sending _
	//:d:en:a [tt]broadcast[/tt] token. The caller can decide to wether or not _
	//:d:en:included in the broadcast and if he requests a response (optional _
	//:d:en:"one-way" token).
	//:d:en:This method requires the user to be authenticated.
	//:a:en::aPool:String:...
	//:a:en::aText:type:...
	//:a:en::aOptions:Object:...
	//:a:en:aOptions:senderIncluded:Boolean:..., default [tt]false[/tt].
	//:a:en:aOptions:responseRequested:Boolean:..., default [tt]true[/tt].
	//:r:*:::void:none
	broadcastText: function( aPool, aText, aOptions ) {
		var lRes = this.checkLoggedIn();
		var lSenderIncluded = false;
		var lResponseRequested = true;
		if( aOptions ) {
			if( aOptions.senderIncluded ) {
				lSenderIncluded = aOptions.senderIncluded;
			}
			if( aOptions.responseRequested ) {
				lResponseRequested = aOptions.responseRequested;
			}
		}
		if( lRes.code == 0 ) {
			this.sendToken({
				ns: jws.NS_SYSTEM,
				type: "broadcast",
				sourceId: this.fClientId,
				sender: this.fUsername,
				pool: aPool,
				data: aText,
				senderIncluded: lSenderIncluded,
				responseRequested: lResponseRequested
			},
			aOptions
			);
		}
		return lRes;
	},

	//:m:*:echo
	//:d:en:Sends an echo token to the jWebSocket server. The server returns
	//:d:en:the same message with a prefix.
	//:a:en::aData:String:An arbitrary string to be returned by the server.
	//:r:*:::void:none
	echo: function( aData ) {
		var lRes = this.checkWriteable();
		if( lRes.code == 0 ) {
			this.sendToken({
				ns: jws.NS_SYSTEM,
				type: "echo",
				data: aData
			});
		}
		return lRes;
	},

	//:m:*:open
	//:d:en:Tries to establish a connection to the jWebSocket server. Unlike _
	//:d:en:the inherited [tt]open[/tt] method no exceptions is fired in case _
	//:d:en:of an error but a response token is returned.
	//:a:en::aURL:String:URL to the jWebSocket server.
	//:a:en::aOptions:Object:Optional arguments, for details please refer to the open method of the [tt]jWebSocketBaseClient[/tt] class.
	//:r:*:::Object:The response token.
	//:r:*:Object:code:Number:Response code (0 = ok, otherwise error).
	//:r:*:Object:msg:String:"Ok" or error message.
	open: function( aURL, aOptions ) {
		var lRes = this.createDefaultResult();
		try {
			if( aOptions && aOptions.OnToken && typeof aOptions.OnToken == "function" ) {
				this.fOnToken = aOptions.OnToken;
			}
			if( aOptions && aOptions.OnWelcome && typeof aOptions.OnWelcome == "function" ) {
				this.fOnWelcome = aOptions.OnWelcome;
			}
			if( aOptions && aOptions.OnGoodBye && typeof aOptions.OnGoodBye == "function" ) {
				this.fOnGoodBye = aOptions.OnGoodBye;
			}
			// call inherited connect, catching potential exception
			arguments.callee.inherited.call( this, aURL, aOptions );
		} catch( ex ) {
			lRes.code = -1;
			lRes.localeKey = "jws.jsc.ex";
			lRes.args = [ ex.message ];
			lRes.msg = "Exception on open: " + ex.message;
		}
		return lRes;
	},

	//:m:*:connect
	//:d:en:Deprecated, kept for upward compatibility only. Do not use anymore!
	//:d:en:Please refer to the [tt]open[/tt] method.
	//:a:en:::Deprecated:Please refer to the [tt]open[/tt] method.
	//:r:*:::Deprecated:Please refer to the [tt]open[/tt] method.
	connect: function( aURL, aOptions ) {
		return this.open( aURL, aOptions );
	},

	//:m:*:close
	//:d:en:Closes an established WebSocket connection.
	//:a:en::aOptions:Object:Optional arguments as listed below...
	//:a:en:aOptions:timeout:Number:Timeout in milliseconds.
	//:r:*:::void:none
	close: function( aOptions ) {
		var lTimeout = 0;

		var lNoGoodBye = false;
		var lNoLogoutBroadcast = false;
		var lNoDisconnectBroadcast = false;

		// turn on isExplicitClose flag to not auto re-connect in case
		// of an explicit, i.e. desired client side close operation
		if( this.fReliabilityOptions ) {
			this.fReliabilityOptions.isExplicitClose = true;
		}
		
		if( aOptions ) {
			if( aOptions.timeout ) {
				lTimeout = aOptions.timeout;
			}
			if( aOptions.noGoodBye ) {
				lNoGoodBye = true;
			}
			if( aOptions.noLogoutBroadcast ) {
				lNoLogoutBroadcast = true;
			}
			if( aOptions.noDisconnectBroadcast ) {
				lNoDisconnectBroadcast = true;
			}
		}
		var lRes = this.checkConnected();
		try {
			// if connected and timeout is passed give server a chance to
			// register the disconnect properly and send a good bye response.
			if( lRes.code == 0 ) {
				var lToken = {
					ns: jws.NS_SYSTEM,
					type: "close",
					timeout: lTimeout
				};
				// only add the following optional fields to
				// the close token on explicit request
				if( lNoGoodBye ) {
					lToken.noGoodBye = true;
				}
				if( lNoLogoutBroadcast ) {
					lToken.noLogoutBroadcast = true;
				}
				if( lNoDisconnectBroadcast ) {
					lToken.noDisconnectBroadcast = true;
				}
				this.sendToken( lToken );
				// call inherited disconnect, catching potential exception
				arguments.callee.inherited.call( this, aOptions );
			} else {
				lRes.code = -1;
				lRes.localeKey = "jws.jsc.res.notConnected";
				lRes.msg = "Not connected.";
			}
		} catch( ex ) {
			lRes.code = -1;
			lRes.localeKey = "jws.jsc.ex";
			lRes.args = [ ex.message ];
			lRes.msg = "Exception on close: " + ex.message;
		}
		return lRes;
	},

	//:m:*:disconnect
	//:d:en:Deprecated, kept for upward compatibility only. Do not use anymore!
	//:d:en:Please refer to the [tt]close[/tt] method.
	//:a:en:::Deprecated:Please refer to the [tt]close[/tt] method.
	//:r:*:::Deprecated:Please refer to the [tt]close[/tt] method.
	disconnect: function( aOptions ) {
		return this.close( aOptions );
	}

});


//	---------------------------------------------------------------------------
//  jWebSocket Client System Plug-In
//	---------------------------------------------------------------------------

//:package:*:jws
//:class:*:jws.SystemClientPlugIn
//:ancestor:*:-
//:d:en:Implementation of the [tt]jws.SystemClientPlugIn[/tt] class.
jws.SystemClientPlugIn = {

	//:const:*:NS:String:org.jwebsocket.plugins.system (jws.NS_BASE + ".plugins.system")
	//:d:en:Namespace for SystemClientPlugIn
	// if namespace changed update server plug-in accordingly!
	NS: jws.NS_SYSTEM,

	//:const:*:ALL_CLIENTS:Number:0
	//:d:en:For [tt]getClients[/tt] method: Returns all currently connected clients irrespective of their authentication state.
	ALL_CLIENTS: 0,
	//:const:*:AUTHENTICATED:Number:1
	//:d:en:For [tt]getClients[/tt] method: Returns all authenticated clients only.
	AUTHENTICATED: 1,
	//:const:*:NON_AUTHENTICATED:Number:2
	//:d:en:For [tt]getClients[/tt] method: Returns all non-authenticated clients only.
	NON_AUTHENTICATED: 2,
	
	//:const:*:PW_PLAIN:Number:null
	//:d:en:Use no password encoding, password is passed as plain text.
	PW_PLAIN		: null,
	//:const:*:PW_ENCODE_MD5:Number:1
	//:d:en:Use MD5 password encoding, password is given as plain but converted and passed as a MD5 hash.
	PW_ENCODE_MD5	: 1,
	//:const:*:PW_MD5_ENCODED:Number:2
	//:d:en:Use MD5 password encoding, password is given and passed as a MD5 hash. _
	//:d:en:The method relies on the correct encoding and does not check the hash.
	PW_MD5_ENCODED	: 2,

	//:m:*:processToken
	//:d:en:Processes an incoming token. Checks if certains events are supposed to be fired.
	//:a:en::aToken:Object:Token to be processed by the plug-ins in the plug-in chain.
	//:r:*:::void:none
	processToken: function( aToken ) {

		// is it a token from the system plug-in at all?
		if( jws.NS_SYSTEM == aToken.ns ) {
			if( "login" == aToken.reqType ) {
				if( aToken.code == 0 ) {
					if( this.fOnLoggedIn ) {
						this.fOnLoggedIn( aToken );
					}
				} else {
					if( this.fOnLoginError ) {
						this.fOnLoginError( aToken );
					}
				}
			} else if( "logout" == aToken.reqType ) {
				if( aToken.code == 0 ) {
					if( this.fOnLoggedOut ) {
						this.fOnLoggedOut( aToken );
					}
				} else {
					if( this.fOnLogoutError ) {
						this.fOnLogoutError( aToken );
					}
				}
			}
		}
	},

	//:m:*:login
	//:d:en:Tries to authenticate the client against the jWebSocket Server by _
	//:d:en:sending a [tt]login[/tt] token.
	//:a:en::aUsername:String:The login name of the user.
	//:a:en::aPassword:String:The password of the user.
	//:a:en::aOptions:Object:Optional arguments as listed below...
	//:a:en:aOptions:pool:String:Default pool the user want to register at (default [tt]null[/tt], no pool).
	//:a:en:aOptions:autoConnect:Boolean:not yet supported (defautl [tt]true[/tt]).
	//:r:*:::void:none
	login: function( aUsername, aPassword, aOptions ) {
		var lPool = null;
		var lEncoding = null;
		if( aOptions ) {
			if( aOptions.pool !== undefined ) {
				lPool = aOptions.pool;
			}
			if( aOptions.encoding !== undefined ) {
				lEncoding = aOptions.encoding;
				// check if password has to be converted into a MD5 sum
				if( jws.SystemClientPlugIn.PW_ENCODE_MD5 == lEncoding ) {
					if( aPassword ) {
						aPassword = jws.tools.calcMD5( aPassword );
					}
					lEncoding = "md5";
				// check if password is already md5 encoded
				} else if( jws.SystemClientPlugIn.PW_MD5_ENCODED == lEncoding ) {
					lEncoding = "md5";
				} else {
					// TODO: raise error here due to invalid encoding option
					lEncoding = null;
				}
			}
		}
		var lRes = this.createDefaultResult();
		if( this.isOpened() ) {
			this.sendToken({
				ns: jws.SystemClientPlugIn.NS,
				type: "login",
				username: aUsername,
				password: aPassword,
				encoding: lEncoding,
				pool: lPool
			});
		} else {
			lRes.code = -1;
			lRes.localeKey = "jws.jsc.res.notConnected";
			lRes.msg = "Not connected.";
		}
		return lRes;
	},

	//:m:*:logon
	//:d:en:Tries to connect and authenticate the client against the _
	//:d:en:jWebSocket Server in a single call. If the client is already _
	//:d:en:connected this connection is used and not re-established. _
	//:d:en:If the client is already authenticated he is logged off first and _
	//:d:en:re-logged in afterwards by sending a [tt]login[/tt] token.
	//:d:en:The logoff of the client in case of a re-login is automatically _
	//:d:en:processed by the jWebSocket server and does not need to be _
	//:d:en:explicitely triggered by the client.
	// TODO: check server if it sends logout event in ths case!
	//:a:en::aURL:String:The URL of the jWebSocket Server.
	//:a:en::aUsername:String:The login name of the user.
	//:a:en::aPassword:String:The password of the user.
	//:a:en::aOptions:Object:Optional arguments as listed below...
	// TODO: document options!
	//:r:*:::void:none
	logon: function( aURL, aUsername, aPassword, aOptions ) {
		var lRes = this.createDefaultResult();
		if( !aOptions ) {
			aOptions = {};
		}
		// if already connected, just send the login token 
		if( this.isOpened() ) {
			this.login( aUsername, aPassword, aOptions );
		} else {
			var lAppOnWelcomeClBk = aOptions.OnWelcome;
			var lThis = this;
			aOptions.OnWelcome = function( aEvent ) {
				if( lAppOnWelcomeClBk ) {
					lAppOnWelcomeClBk.call( lThis, aEvent );
				}
				lThis.login( aUsername, aPassword, aOptions );
			};
			this.open(
				aURL,
				aOptions
			);
		}
		return lRes;
	},

	//:m:*:logout
	//:d:en:Logs the currently authenticated used out. After that the user _
	//:d:en:is not authenticated anymore against the jWebSocket network. _
	//:d:en:The client is not automatically disconnected.
	//:d:en:If you want to logout and disconnect please refere to the _
	//:d:en:[tt]close[/tt] method. Closing a connection automatically logs off _
	//:d:en:a potentially authenticated user.
	// TODO: implement optional auto disconnect!
	//:a:en::::none
	//:r:*:::void:none
	logout: function() {
		var lRes = this.checkConnected();
		if( lRes.code == 0 ) {
			this.sendToken({
				ns: jws.SystemClientPlugIn.NS,
				type: "logout"
			});
		}
		return lRes;
	},

	//:m:*:isLoggedIn
	//:d:en:Returns [tt]true[/tt] when the client is authenticated, _
	//:d:en:otherwise [tt]false[/tt].
	//:a:en::::none
	//:r:*:::Boolean:[tt]true[/tt] when the client is authenticated, otherwise [tt]false[/tt].
	isLoggedIn: function() {
		return( this.isOpened() && this.fUsername );
	},

	broadcastToken: function( aToken, aOptions ) {
		aToken.ns = jws.SystemClientPlugIn.NS;
		aToken.type = "broadcast";
		aToken.sourceId = this.fClientId;
		aToken.sender = this.fUsername;
		return this.sendToken( aToken, aOptions );
	},

	//:m:*:getUsername
	//:d:en:Returns the login name when the client is authenticated, _
	//:d:en:otherwise [tt]null[/tt].
	//:d:en:description pending...
	//:a:en::::none
	//:r:*:::String:Login name when the client is authenticated, otherwise [tt]null[/tt].
	getUsername: function() {
		return( this.isLoggedIn() ? this.fUsername : null );
	},

	//:m:*:getClients
	//:d:en:Returns an array of clients that are currently connected to the
	//:d:en:jWebSocket network by using the [tt]getClients[/tt] token.
	//:d:en:Notice that the call is non-blocking, i.e. the clients are _
	//:d:en:returned asynchronously by the OnResponse event.
	//:a:en::aOptions:Object:Optional arguments as listed below...
	// TODO: support and/or check pool here!
	//:a:en:aOptions:pool:String:Only consider connections to that certain pool (default=[tt]null[/tt]).
	//:a:en:aOptions:mode:Number:One of the following constants [tt]AUTHENTICATED[/tt], [tt]NON_AUTHENTICATED[/tt], [tt]ALL_CLIENTS[/tt].
	//:r:*:::void:none
	getClients: function( aOptions ) {
		var lMode = jws.SystemClientPlugIn.ALL_CLIENTS;
		var lPool = null;
		if( aOptions ) {
			if( aOptions.mode == jws.SystemClientPlugIn.AUTHENTICATED ||
				aOptions.mode == jws.SystemClientPlugIn.NON_AUTHENTICATED ) {
				lMode = aOptions.mode;
			}
			if( aOptions.pool ) {
				lPool = aOptions.pool;
			}
		}
		var lRes = this.createDefaultResult();
		if( this.isLoggedIn() ) {
			this.sendToken({
				ns: jws.SystemClientPlugIn.NS,
				type: "getClients",
				mode: lMode,
				pool: lPool
			});
		} else {
			lRes.code = -1;
			lRes.localeKey = "jws.jsc.res.notLoggedIn";
			lRes.msg = "Not logged in.";
		}
		return lRes;
	},

	//:m:*:getNonAuthClients
	//:d:en:Requests an array of all clients that are currently connected to _
	//:d:en:the jWebSocket network but not authenticated.
	//:d:en:Notice that the call is non-blocking, i.e. the clients are _
	//:d:en:returned asynchronously by the OnResponse event.
	//:a:en::aOptions:Object:Please refer to the [tt]getClients[/tt] method.
	//:r:*:::void:none
	getNonAuthClients: function( aOptions ) {
		if( !aOptions ) {
			aOptions = {};
		}
		aOptions.mode = jws.SystemClientPlugIn.NON_AUTHENTICATED;
		return this.getClients( aOptions );
	},

	//:m:*:getAuthClients
	//:d:en:Requests an array of all clients that are currently connected to _
	//:d:en:the jWebSocket network and that are authenticated.
	//:d:en:Notice that the call is non-blocking, i.e. the clients are _
	//:d:en:returned asynchronously by the OnResponse event.
	//:a:en::aOptions:Object:Please refer to the [tt]getClients[/tt] method.
	//:r:*:::void:none
	getAuthClients: function( aOptions ) {
		if( !aOptions ) {
			aOptions = {};
		}
		aOptions.mode = jws.SystemClientPlugIn.AUTHENTICATED;
		return this.getClients( aOptions );
	},

	//:m:*:getAllClients
	//:d:en:Requests an array of all clients that are currently connected to _
	//:d:en:the jWebSocket network irrespective of their authentication status.
	//:d:en:Notice that the call is non-blocking, i.e. the clients are _
	//:d:en:returned asynchronously by the OnResponse event.
	//:a:en::aOptions:Object:Please refer to the [tt]getClients[/tt] method.
	//:r:*:::void:none
	getAllClients: function( aOptions ) {
		if( !aOptions ) {
			aOptions = {};
		}
		aOptions.mode = jws.SystemClientPlugIn.ALL_CLIENTS;
		return this.getClients( aOptions );
	},

	//:m:*:ping
	//:d:en:Sends a simple [tt]ping[/tt] token to the jWebSocket Server as a _
	//:d:en:notification that the client is still alive. The client optionally _
	//:d:en:can request an echo so that the client also get a notification _
	//:d:en:that the server still is alive. The [tt]ping[/tt] thus is an _
	//:d:en:important part of the jWebSocket connection management.
	//:a:en::aOptions:Object:Optional arguments as listed below...
	//:a:en:aOptions:echo:Boolean:Specifies whether the client expects a response from the server (default=[tt]true[/tt]).
	//:r:*:::void:none
	ping: function( aOptions ) {
		var lEcho = false;
		if( aOptions ) {
			if( aOptions.echo ) {
				lEcho = true;
			}
		}
		var lRes = this.createDefaultResult();
		if( this.isOpened() ) {
			this.sendToken({
				ns: jws.SystemClientPlugIn.NS,
				type: "ping",
				echo: lEcho
				},
				aOptions
			);
		} else {
			lRes.code = -1;
			lRes.localeKey = "jws.jsc.res.notConnected";
			lRes.msg = "Not connected.";
		}
		return lRes;
	},

	//:m:*:wait
	//:d:en:Simply send a wait request to the jWebSocket server. _
	//:d:en:The server waits for the given amount of time and returns a _
	//:d:en:result token. This feature is for test and debugging purposes only _
	//:d:en:and is not related to any particular business logic.
	//:a:en::aDuration:Integer:Duration in ms the server waits for a response
	//:a:en::aOptions:Object:Optional arguments as listed below...
	//:a:en:aOptions:OnResponse:Function:Callback to be invoked once the response is received.
	//:r:*:::void:none
	wait: function( aDuration, aOptions ) {
		var lRes = this.checkConnected();
		if( lRes.code == 0 ) {
			var lResponseRequested = true;
			if( aOptions ) {
				if( aOptions.responseRequested != undefined ) {
					lResponseRequested = aOptions.responseRequested;
				}
			}
			this.sendToken({
				ns: jws.SystemClientPlugIn.NS,
				type: "wait",
				duration: aDuration,
				responseRequested: lResponseRequested
				},
				aOptions
			);
		}
		return lRes;
	},

	//:m:*:startKeepAlive
	//:d:en:Starts the keep-alive timer in background. keep-alive sends _
	//:d:en:periodic pings to the server with an configurable interval.
	//:d:en:If the keep-alive timer has already has been started, the previous _
	//:d:en:one will be stopped automatically and a new one with new options _
	//:d:en:will be initiated.
	//:a:en::aOptions:Objects:Optional arguments as listed below...
	//:a:en:aOptions:interval:Number:Number of milliseconds for the interval.
	//:a:en:aOptions:echo:Boolean:Specifies wether the server is supposed to send an answer to the client.
	//:a:en:aOptions:immediate:Boolean:Specifies wether to send the first ping immediately or after the first interval.
	//:r:*:::void:none
	startKeepAlive: function( aOptions ) {
		// if we have a keep alive running already stop it
		if( this.hKeepAlive ) {
			stopKeepAlive();
		}
		// return if not (yet) connected
		if( !this.isOpened() ) {
			// TODO: provide reasonable result here!
			return;
		}
		var lInterval = 10000;
		var lEcho = true;
		var lImmediate = true;
		if( aOptions ) {
			if( aOptions.interval != undefined ) {
				lInterval = aOptions.interval;
			}
			if( aOptions.echo != undefined ) {
				lEcho = aOptions.echo;
			}
			if( aOptions.immediate != undefined ) {
				lImmediate = aOptions.immediate;
			}
		}
		if( lImmediate ) {
			// send first ping immediately, if requested
			this.ping({
				echo: lEcho
			});
		}
		// and then initiate interval...
		var lThis = this;
		this.hKeepAlive = setInterval(
			function() {
				if( lThis.isOpened() ) {
					lThis.ping({
						echo: lEcho
					});
				} else {
					lThis.stopKeepAlive();
				}
			},
			lInterval
		);
	},

	//:m:*:stopKeepAlive
	//:d:en:Stops the keep-alive timer in background. If no keep-alive is _
	//:d:en:running no operation is performed.
	//:a:en::::none
	//:r:*:::void:none
	stopKeepAlive: function() {
		// TODO: return reasonable results here
		if( this.hKeepAlive ) {
			clearInterval( this.hKeepAlive );
			this.hKeepAlive = null;
		}
	},

	setSystemCallbacks: function( aListeners ) {
		if( !aListeners ) {
			aListeners = {};
		}
		if( aListeners.OnLoggedIn !== undefined ) {
			this.fOnLoggedIn = aListeners.OnLoggedIn;
		}
		if( aListeners.OnLoginError !== undefined ) {
			this.fOnLoginError = aListeners.OnLoginError;
		}
		if( aListeners.OnLoggedOut !== undefined ) {
			this.fOnLoggedOut = aListeners.OnLoggedOut;
		}
		if( aListeners.OnLogoutError !== undefined ) {
			this.fOnLogoutError = aListeners.OnLogoutError;
		}
	}
};

// add the JWebSocket SystemClient PlugIn into the BaseClient class
jws.oop.addPlugIn( jws.jWebSocketTokenClient, jws.SystemClientPlugIn );


//	---------------------------------------------------------------------------
//  jWebSocket JSON client
//	todo: consider potential security issues with 'eval'
//	---------------------------------------------------------------------------

//:package:*:jws
//:class:*:jws.jWebSocketJSONClient
//:ancestor:*:jws.jWebSocketTokenClient
//:d:en:Implementation of the [tt]jws.jWebSocketJSONClient[/tt] class.
jws.oop.declareClass( "jws", "jWebSocketJSONClient", jws.jWebSocketTokenClient, {

	//:m:*:tokenToStream
	//:d:en:converts a token to a JSON stream. If the browser provides a _
	//:d:en:native JSON class this is used, otherwise it use the automatically _
	//:d:en:embedded JSON library from json.org.
	//:a:en::aToken:Token:The token (an JavaScript Object) to be converted into an JSON stream.
	//:r:*:::String:The resulting JSON stream.
	tokenToStream: function( aToken ) {
		aToken.utid = jws.CUR_TOKEN_ID;
		var lJSON = JSON.stringify( aToken );
 		return( lJSON );
	},

	//:m:*:streamToToken
	//:d:en:converts a JSON stream into a token. If the browser provides a _
	//:d:en:native JSON class this is used, otherwise it use the automatically _
	//:d:en:embedded JSON library from json.org. For security reasons the _
	//:d:en:use of JavaScript's eval explicitely was avoided.
	//:a:en::aStream:String:The data stream received from the server to be parsed as JSON.
	//:r:*::Token:Object:The Token object of stream could be parsed successfully.
	//:r:*:Token:[i]field[/i]:[i]type[/i]:Fields of the token depend on its content and purpose and need to be interpreted by the higher level software tiers.
	streamToToken: function( aStream ) {
		// parsing a JSON object in JavaScript couldn't be simpler...
		var lObj = JSON.parse( aStream );
		return lObj;
	}

});


//	---------------------------------------------------------------------------
//  jWebSocket CSV client
//	todo: implement jWebSocket JavaScript CSV client
//	jWebSocket target release 1.1
//	---------------------------------------------------------------------------

//:package:*:jws
//:class:*:jws.jWebSocketCSVClient
//:ancestor:*:jws.jWebSocketTokenClient
//:d:en:Implementation of the [tt]jws.jWebSocketCSVClient[/tt] class.
jws.oop.declareClass( "jws", "jWebSocketCSVClient", jws.jWebSocketTokenClient, {

	// todo: implement escaping of command separators and equal signs
	//:m:*:tokenToStream
	//:d:en:converts a token to a CSV stream.
	//:a:en::aToken:Token:The token (an JavaScript Object) to be converted into an CSV stream.
	//:r:*:::String:The resulting CSV stream.
	tokenToStream: function( aToken ) {
		var lCSV = "utid=" + jws.CUR_TOKEN_ID;
		for( var lKey in aToken ) {
			var lVal = aToken[ lKey ];
			if( lVal === null || lVal === undefined ) {
				// simply do not generate a value, keep value field empty
				lCSV += "," + lKey + "=";
			} else if( typeof lVal == "string" ) {
				// escape commata and quotes
				lVal = lVal.replace( /[,]/g, "\\x2C" );
				lVal = lVal.replace( /["]/g, "\\x22" );
				lCSV += "," + lKey + "=\"" + lVal + "\"";
			} else {
				lCSV += "," + lKey + "=" + lVal;
			}
		}
		return lCSV;
	},

	// todo: implement escaping of command separators and equal signs
	//:m:*:streamToToken
	//:d:en:converts a CSV stream into a token.
	//:a:en::aStream:String:The data stream received from the server to be parsed as CSV.
	//:r:*::Token:Object:The Token object of stream could be parsed successfully.
	//:r:*:Token:[i]field[/i]:[i]type[/i]:Fields of the token depend on its content and purpose and need to be interpreted by the higher level software tiers.
	streamToToken: function( aStream ) {
		var lToken = {};
		var lItems = aStream.split(",");
		for( var lIdx = 0, lCnt = lItems.length; lIdx < lCnt; lIdx++ ) {
			var lKeyVal = lItems[ lIdx ].split( "=" );
			if( lKeyVal.length == 2 ) {
				var lKey = lKeyVal[ 0 ];
				var lVal = lKeyVal[ 1 ];
				if( lVal.length >= 2 
					&& lVal.charAt(0)=="\""
					&& lVal.charAt(lVal.length-1)=="\"" ) {
					// unescape commata and quotes
					lVal = lVal.replace( /\\x2C/g, "\x2C" );
					lVal = lVal.replace( /\\x22/g, "\x22" );
					// strip string quotes
					lVal = lVal.substr( 1, lVal.length - 2 );
				}
				lToken[ lKey ] = lVal;
			}
		}
		return lToken;
	}

});


//	---------------------------------------------------------------------------
//  jWebSocket XML client
//	todo: PRELIMINARY! Implement jWebSocket JavaScript XML client
//	Targetted for jWebSocket release 1.1
//	---------------------------------------------------------------------------

//:package:*:jws
//:class:*:jws.jWebSocketXMLClient
//:ancestor:*:jws.jWebSocketTokenClient
//:d:en:Implementation of the [tt]jws.jWebSocketXMLClient[/tt] class.
jws.oop.declareClass( "jws", "jWebSocketXMLClient", jws.jWebSocketTokenClient, {

	//:m:*:tokenToStream
	//:d:en:converts a token to a XML stream.
	//:a:en::aToken:Token:The token (an JavaScript Object) to be converted into an XML stream.
	//:r:*:::String:The resulting XML stream.
	tokenToStream: function( aToken ) {

		function obj2xml( aKey, aValue ) {
			var lXML = "";
			// do we have an array? Caution! Keep this condition on
			// the top because array is also an object!
			if ( aValue instanceof Array ) {
				lXML += "<" + aKey + " type=\"" + "array" + "\">";
				for( var lIdx = 0, lCnt = aValue.length; lIdx < lCnt; lIdx++ ) {
					lXML += obj2xml( "item", aValue[ lIdx ] );
				}
				lXML += "</" + aKey + ">";
			}
			// or do we have an object?
			else if ( typeof aValue  == "object" ) {
				lXML += "<" + aKey + " type=\"" + "object" + "\">";
				for(var lField in aValue ) {
					lXML += obj2xml( lField, aValue[ lField ] );
				}
				lXML += "</" + aKey + ">";
			}
			// or do we have a plain field?
			else {
				lXML +=
				"<" + aKey + " type=\"" + typeof aValue + "\">" +
				aValue.toString() +
				"</" + aKey + ">";
			}
			return lXML;
		}

		var lEncoding = "windows-1252";
		var lResXML =
		"<?xml version=\"1.0\" encoding=\"" + lEncoding + "\"?>" +
		"<token>";
		for( var lField in aToken ) {
			lResXML += obj2xml( lField, aToken[ lField ] );
		}
		lResXML += "</token>";
		return lResXML;
	},

	//:m:*:streamToToken
	//:d:en:converts a XML stream into a token.
	//:a:en::aStream:String:The data stream received from the server to be parsed as XML.
	//:r:*::Token:Object:The Token object of stream could be parsed successfully.
	//:r:*:Token:[i]field[/i]:[i]type[/i]:Fields of the token depend on its content and purpose and need to be interpreted by the higher level software tiers.
	streamToToken: function( aStream ) {
		// first convert the stream into an XML document 
		// by using the embedded XML parser.
		// We do not really want to parse the XML in Javascript!
		// Using the built-in parser should be more performant.
		var lDoc = null;
		/* Once we have an applet for IEx ;-)
		if( window.ActiveXObject ) {
			//:i:de:Internet Explorer
			lDoc = new ActiveXObject( "Microsoft.XMLDOM" );
			lDoc.async = "false";
			lDoc.loadXML( aStream );
		} else {
*/
		// For all other Browsers
		try{
			var lParser = new DOMParser();
			lDoc = lParser.parseFromString( aStream, "text/xml" );
		} catch( ex ) {
		// ignore exception here, lDoc will keep being null
		}
		/*
		}
*/

		function node2obj( aNode, aObj ) {
			var lNode = aNode.firstChild;
			while( lNode != null ) {
				// 1 = element node
				if( lNode.nodeType == 1 ) {
					var lType = lNode.getAttribute( "type" );
					var lKey = lNode.nodeName;
					if( lType ) {
						var lValue = lNode.firstChild;
						// 3 = text node
						if( lValue && lValue.nodeType == 3 ) {
							lValue = lValue.nodeValue;
							if( lValue ) {
								if( lType == "string" ) {
								} else if( lType == "number" ) {
								} else if( lType == "boolean" ) {
								} else if( lType == "date" ) {
								} else {
									lValue = undefined;
								}
								if( lValue ) {
									if ( aObj instanceof Array ) {
										aObj.push( lValue );
									} else {
										aObj[ lKey ] = lValue;
									}
								}
							}
						} else
						// 1 = element node
						if( lValue && lValue.nodeType == 1 ) {
							if( lType == "array" ) {
								aObj[ lKey ] = [];
								node2obj( lNode, aObj[ lKey ] );
							} else if( lType == "object" ) {
								aObj[ lKey ] = {};
								node2obj( lNode, aObj[ lKey ] );
							}
						}
					}
				}
				lNode = lNode.nextSibling;
			}
		}

		var lToken = {};
		if( lDoc ) {
			node2obj( lDoc.firstChild, lToken );
		}
		return lToken;
	}

});

/*
(function() {
	var lObj = {
		aNumber: 1,
		aString: "test1",
		aBoolean: true,
		aArray: [ 2, "test2", false ],
		aObject: {
			bNumber: 3,
			bString: "test3",
			bBoolean: true,
			bArray: [ 3, "test3", true ]
		}
	};
	var lStream = 
		'<?xml version="1.0" encoding="windows-1252"?>' +
		'<token>' +
			'<aNumber type="number">1</aNumber>' +
			'<aString type="string">test1</aString>' +
			'<aBoolean type="boolean">true</aBoolean>' +
			'<aArray type="array">' +
				'<item type="number">2</item>'+
				'<item type="string">test2</item>' +
				'<item type="boolean">false</item>' +
			'</aArray>' +
			'<aObject type="object">' +
				'<bNumber type="number">3</bNumber>'+
				'<bString type="string">test3</bString>' +
				'<bBoolean type="boolean">true</bBoolean>' +
				'<bArray type="array">'+
					'<item type="number">3</item>' +
					'<item type="string">test3</item>' +
					'<item type="boolean">true</item>' +
				'</bArray>' +
			'</aObject>' +
		'</token>';

	var lXMLClient = new jws.jWebSocketXMLClient();
//	var lStream = lXMLClient.tokenToStream( lObj );
	var lToken = lXMLClient.streamToToken( lStream );
	console.log( lStream );
})();
*/
