



if( !window.XMLHttpRequest ) XMLHttpRequest = function(){
        try{ return new ActiveXObject("MSXML3.XMLHTTP") }catch(e){}
        try{ return new ActiveXObject("MSXML2.XMLHTTP.3.0") }catch(e){}
        try{ return new ActiveXObject("Msxml2.XMLHTTP") }catch(e){}
        try{ return new ActiveXObject("Microsoft.XMLHTTP") }catch(e){}
        return null ;
};

imports = {} ;
function classify(o,protoClass) {
	if (!protoClass)
		protoClass = imports[o.java$class] ;
	if (protoClass && protoClass.prototype) {
		if (o.__proto__)
			o.__proto__ = protoClass.prototype ; /* This works in FF and is faster (provide a reference to the prototypical members, don't copy) */
		else { 
			/* Not FF or similar JS engine - use "clean" constructor call via new and copy in the properties */
			var instance = new protoClass ;
			for (var i in o) instance[i] = o[i] ;
			return instance ; 
		}
	}
	return o ;
}

function myUnescape(str) {
	return str?decodeURIComponent(str.replace(/\+/g," ")):(str==""?"":null) ;
}

/* JSCastingPrototypeBuilder(com.flirtomatic.misc.JSImport) */
var JSImport = function() { this.java$class=arguments.callee.java$class };
JSImport.java$class='com.flirtomatic.misc.JSImport';
imports['com.flirtomatic.misc.JSImport'] = JSImport;
JSImport.classes = function( boolean_1, ClassArray_2,_return,_cb,_except) { return rJS.Exec(this.java$class,rJS.Call('classes', rJS.Boolean(boolean_1), rJS.JavaClassArray(ClassArray_2)),_return,_cb||this,_except,rJS.MethodCall); }
/* end { JSImport } */



/** Main namespace functions **/
var rJS = {
	method:"POST",	
	syncMode: true, 	// Set to TRUE to make the default mode synchronous if no handler is defined. Normally asynchronous if no handler is defined (i.e. return is discarded) 
	responseClass: 'com.flirtomatic.wyll.JSON',
	getBaseCallURL:function(){ return 'http://www.flirtomatic.com/flirto/cls!C271/rjsmi.jsp' ; },
	getLocalCallURL:function(){ return 'http://www.flirtomatic.com/flirto/cls!C271/rjsmi.jsp' ; },
	callURL:  'http://www.flirtomatic.com/flirto/cls!C271/rjsmi.jsp',

	importClass:function(classDefn) {
		var jsonClass = function() {  this.java$class=arguments.callee.java$class  }
		var values ;
		var members ;
		for (var s in classDefn) {
			if (s=='$values')
				values = classDefn[s] ;
			else if (s=='$members') {
				members = classDefn[s] ;
				for (var m in members)
					jsonClass.prototype[m] = members[m] ; 
			} else
				jsonClass[s]  = classDefn[s] ;
		} 
		if (values) {
			for (var m in values) 
				if (values[m]!='java$class')
					jsonClass[m] = members?classify(values[m],jsonClass):values[m] ;
		}

		imports[jsonClass.java$class.replace(new RegExp("\\\$","g"),"-")] = jsonClass ;
		if (values)
			delete jsonClass.java$class ;
		return jsonClass;
	},
	importClasses:function(jsonClassArray,scope) {
		if (!scope) scope = window ;
		for (var n in jsonClassArray) {
			scope[n] = rJS.importClass(jsonClassArray[n]) ;
		}
		return imports ;
	},
	
	isArray: function (o) {
		return o.constructor==Array ;
	}, 
	
	xmlEncode: function(x) {
		return x.replace(new RegExp("&","g"),"&amp;").replace(new RegExp(">","g"),"&gt;").replace(new RegExp("<","g"),"&lt;") ;
	},
	
	xmlDecode: function(x) {
		return (x==undefined || x==null)?x:x.toString().replace(new RegExp("&amp;","g"),"&").replace(new RegExp("&gt;","g"),">").replace(new RegExp("&lt;","g"),"<") ;
	},
	
	xstreamField: function(tag,z,className) {
		if (z==null) 
			return "";
		
		var request = tag?('<'+tag+(className ? (' class="'+className+'"') :'' )+'>'):'' ;
		switch (typeof z)
		{
		  case 'undefined':
			return "</"+tag+">" ;
		  case 'boolean':
			request += (z?"true":"false") ;	
			break ;
		  case  'function': // Type cast
			if (!z.isCast)
				return "" ; 	// Method
			request += z() ;
		    break ;
		  case 'object':	
		  	if (z.java$class=='java.sql.Timestamp')
		  	{
				function zeroFill(n,l) { return ("00000000000".substring(0,l-n.toString().length))+n.toString(); } ;
	
		  		var d = new Date(z.ms) ;
		  		request += ""+zeroFill(d.getFullYear(),4)+"-"+zeroFill(d.getMonth()+1,2)+"-"+zeroFill(d.getDate(),2)+" "+zeroFill(d.getHours(),2)+":"+zeroFill(d.getMinutes(),2)+":"+zeroFill(d.getSeconds(),2) ;
		  	}
		  	else
		  	{
				if (this.isArray(z)) // Is it an array?
				{
			  		for (var j in z) if (j!='java$array')
			  			request += this.xstream(z[j]) ;
				}
				else
				{ 
				  	for (var j in z) if (j!='java$class')
			  		{
						var xmlClassName = j.replace(/\_/g,"__") ; 
				  		request += this.xstreamField(xmlClassName,z[j],z[j]?z[j].java$class:null) ;
				  	}
				}
			}
			break ;
		  case 'string':
			request += this.xmlEncode(z) ;
			break ;
		  case 'number':
		  default:
			request += z ;
			break ;
		}
		if (tag)
			request += "</"+tag+">" ;
		return request ;
	},
	 
	// Convert a single argument to XStream format
	xstream: function(z) {
		if (z==null) return "<null/>" ;
		var request = "" ;
		switch (typeof z) {
		  case 'undefined':
			request += "<null/>" ;
			break ;
		  case 'number':
			request += "<long>"+z+"</long>" ;
			break ;
		  case 'boolean':
			request += "<boolean>"+(z?"true":"false")+"</boolean>" ;	
			break ;
		  case 'string':
			request += "<string>"+this.xmlEncode(z)+"</string>" ;	
			break ;
		  case  'function': // Type cast
		    if (!z.isCast)
				return "" ; // Method
			request += z() ;
		    break ;
		  case 'object':	
			if (z.java$class) {	// If JSON'd
		  		request += this.xstreamField(z.java$class,z) ;
		  	} else { // Native JS object - no type, just a collection of (named) things - maybe an array
				if (this.isArray(z)) { // Is it an array?
					var componentType = z.java$array ;
					if (!componentType)
						componentType = 'long' ; // No idea what this array consists of
			  		for (var j in z) if (j!='java$array')
			  			request += "<"+componentType+">"+this.xstream(z[j])+"</"+componentType+">" ;
				} else {
					// A JS object - just serailise it
			  		for (var j in z)
			  			request += "<"+j+">"+this.xstream([j])+"</"+j+">" ;
				}
		  	}
			break ;
		  default:
			request += "<string>"+this.xmlEncode(z.toString())+"</string>" ;
			break ;
		}
		return request ;
	},
	
	Invoke: function(member,args) {
		// Form a "call"
		var request = "<com.flirtomatic.cluster.MethodCall-CallContext><method>"+(member?member:"<null/>")+"</method>" ;
		if (args.length>0) {
	 		request += "<args>" ;
			for (var i = 0; i < args.length; i++) {
	   			request += this.xstream(args[i]) ;
	    		request += "\n" ;
	 		}
	 		request += "</args>" ;
	 	}else{
	 		request += "<args/>" ;
	 	}
		 return request+"</com.flirtomatic.cluster.MethodCall-CallContext>" ;
	},
	
	Call: function(member){
		return this.Invoke(member,Array.prototype.slice.call(arguments,1)) ;
	},
	
	/** Type casts used to give Java types to dodgy JavaScript parameters **/
	typeCast: function(f){
		f.isCast = true ;
		return f ;
	},
	
	Int: function(n) { return this.typeCast(function() { return "<int>"+n+"</int>" ; }) },
	Boolean: function(n) { return this.typeCast(function() { return "<boolean>"+(n?"true":"false")+"</boolean>" ; }) },
	Long: function(n) { return this.typeCast(function() { return "<long>"+n+"</long>" ; }) },
	Double: function(n) { return this.typeCast(function() { return "<double>"+n+"</double>" ; }) },
	Float: function(n) { return this.typeCast(function() { return "<float>"+n+"</float>" ; }) },
	JavaString: function(n) { return this.typeCast(function() { return (n!=null && n!=undefined)?"<string>"+rJS.xmlEncode(n.toString())+"</string>":"<null/>" ; }) },
	JavaClass: function(n) { return this.typeCast(function() { return "<java-class>"+n.replace(new RegExp("-","g"),"$")+"</java-class>" ; }) },
	/* Convert a JS timestamp to a form understandable by JavaScript-Method */
	Timestamp: function(jsDate) {
		return {human: jsDate.toString(), ms: jsDate.getTime(), java$class:'java.sql.Timestamp'}
	},
	
	asArray: function(elem,array,transform) {
		if (!transform) transform = function(x) { return x } ; 
		var request = "<"+elem+"-array>" ; 
		for (var v in array)
			request += "<"+elem+">"+transform(array[v])+"</"+elem+">" ;
		request += "</"+elem+"-array>" ;
		return function() { return request; } 
	},
	
	IntArray: function() { return this.typeCast(this.asArray("int",Array.prototype.slice.call(arguments)[0])) },
	LongArray: function() { return this.typeCast(this.asArray("long",Array.prototype.slice.call(arguments)[0])) },
	DoubleArray: function() { return this.typeCast(this.asArray("double",Array.prototype.slice.call(arguments)[0])) },
	FloatArray: function() { return this.typeCast(this.asArray("float",Array.prototype.slice.call(arguments)[0])) },
	JavaStringArray: function() { return this.typeCast(this.asArray("string",Array.prototype.slice.call(arguments)[0])) },
	JavaClassArray: function() { return this.typeCast(this.asArray("java-class",Array.prototype.slice.call(arguments)[0],function(clz){ return clz.replace(new RegExp("-","g"),"$")})) },
	
	/** Re-define this function if you wish to handle exceptions returned via RMII **/
	rmiiException: function(e,cbData,handler) {
		alert(e.exceptionType+'\n'+e.localizedMessage) ; 
		return undefined ; 
	},
	
	/* Stub "handler" used to indicate we require a synchronous execution and return */
	sync: function(){},
	/* "handler" used to indicate we require asynchronous execution when syncMode is true */
	async: function(x){},
	
	/*MethodCall XML Builders*/
	
	MethodCall: function(obj,callSequence) {var jsmc = "<com.flirtomatic.cluster.MethodCall>" ;if (obj.java$class) jsmc += "<self class=\""+obj.java$class+"\">"+this.xstreamField(null,obj)+"</self><vCC>" ;else jsmc += "<self class=\"java-class\">"+obj+"</self><vCC>" ;jsmc += callSequence ;jsmc += "</vCC></com.flirtomatic.cluster.MethodCall>" ;return jsmc ; },
	SessionMethodCall: function(cl) { return function(obj,callSequence) { var jsmc = "<com.flirtomatic.cluster.SessionMethodCall><self class=\"java-class\">"+cl+"</self>" ; jsmc += "<vCC>"+callSequence+"</vCC>" ; jsmc += "</com.flirtomatic.cluster.SessionMethodCall>" ; return jsmc ; }},
	
	getJSURL: function() { return this.callURL+"?jsbuild="+this.responseClass+"&call" },
	 
	ajaxRequest:function(async,data,url,handler,exceptionHandler,callBackData, headers) {
		//var stack = stackDump().join("\n\n") ;
		
	    var req = new XMLHttpRequest() ;
	    if (req.overrideMimeType)
	      req.overrideMimeType("text/plain");
			
	    if (this.method=="GET") {
		    req.open("GET", url+"="+data, async);
		    data = null ;
		} else {
		    req.open("POST", url, async);
		}
	    req.setRequestHeader("Content-Type", "text/xml");
	    if (headers) for (var i in headers) 
		    req.setRequestHeader(i, headers[i]);
	
	    if (async)
	    {
		    req.onreadystatechange = function() {
		        if (req.readyState == 4) {

					try {

				        var responseStatus = null;
				        try { responseStatus = req.status; } 
				        catch (noStatusException) {}
						if (!responseStatus || responseStatus < 400) {
/*if (req.responseText.length>4999999) {
	remoteDbg("aajax "+req.responseText.length+" "+stack) ;
}*/					
							if (handler && async){
								handler(req.responseText,callBackData) ;
							}
						} else {
			        		throw ({exceptionType:"XMLHttpRequest$Exception", message:responseStatus}) ;
						}

					} catch(ex) {
		        		return exceptionHandler(ex,callBackData,async?handler:null) ;
					}

		        }
		    };
	    }
	    req.send(data) ;
	    if (async)
	    	return null ;
/*	    if (req.responseText && req.responseText.length>4999999) {
			remoteDbg("sajax "+req.responseText.length+" "+stack) ;
		}*/						
		return req.responseText ;
	},
	
	Exec: function(obj,callSequence,handler,callBackData,exceptionHandler,xmlBuilder) {
		var async = (handler!=this.sync) ;
		if (!handler) handler = null ;
		if (!callBackData) callBackData = null ;
		if (!exceptionHandler) exceptionHandler = this.rmiiException ;
		if (handler==null && async && this.syncMode)
			async = false ;
	
		if (!xmlBuilder)	// Backward compatible - this parameter may be omitted, in which case MethodCall is invoked. 
			xmlBuilder = this.MethodCall ;
		var jsmc = xmlBuilder.apply(this,[obj,callSequence]) ;
		

		try {

			var result = this.ajaxRequest(async,jsmc,this.getJSURL(),handler?function(x,y){handler.call(y,eval(x),y)}:null,exceptionHandler,callBackData) ;
			if (!async)
				return eval(result) ;

		} catch(ex) {
       		return exceptionHandler(ex,callBackData,async?handler:null) ;
		}

	}
}

