var AJAX = function(args){
	args = args || {};

	this.success = args.success;
	this.error = args.error;
	this.onReadyStateChange = args.onReadyStateChange;

	this.xhr = this._getXHR();
	this._onReadyStateChange = UTIL.hitch(this, this._onReadyStateChange);
	this.ready = true;
};
AJAX.prototype = {
	xhr : null,		// the XMLHttpRequest object, readonly, do not modify from outside!
	ready : false, 	// is the xhr ready for use? readonly, do not modify from outside!

	onReadyStateChange : null,
	success : null,
	error : null,
	
	_STATE_TEXT : ["Not initiated.", "Preparing...", "Processing...", "Receiving response from server...", "Complete!"],

	_getXHR : function(){
		if(window.XMLHttpRequest){
			return new XMLHttpRequest();
        }else{
            var msxml = ['MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP'];
            for(var i = 0; i < msxml.length; ++i){
            	try{
            		return new ActiveXObject(msxml[i]);
            	}catch(e){}
            }
        }
        
        UTIL.error("Failed to create XMLHttpRequest object!");
        throw new Error("Failed to create XMLHttpRequest object!");
	},
	
	_onReadyStateChange : function(){
	    this._fireReadyStateChange();

		if(this.xhr.readyState == 4){
            if(this.xhr.status){
    		    this.ready = true;

    			if(this.xhr.status == 200){
    				this._fireSuccess();
    			}else{
    				this._fireError();
    			}
            }
		}
	},
	
	_fireReadyStateChange : function(){
	    try{
    		if((typeof this.onReadyStateChange) == "function"){
    			this.onReadyStateChange.call(this, this.xhr.readyState, this._STATE_TEXT[this.xhr.readyState], this.xhr);
    		}
	    }catch(ex){
	        UTIL.error("Error firing onReadyStateChange callback! message: " + ex.message);
	    }
	},
	
	_fireSuccess : function(){
	    try{
    		if((typeof this.success) == "function"){
    			this.success.call(this, "load", this.xhr.responseText, this.xhr);
    		}
	    }catch(ex){
	        UTIL.error("Error firing success callback! message: " + ex.message);
	    }
	},

	_fireError : function(msg){
	    try{
    		if((typeof this.error) == "function"){
    		    var type;
    		    try{
    		        type = this.xhr.status ? "error" : "cancel";
    		    }catch(e){
    		        type = "cancel";
    		    }
    		    var msg = (type == "cancel") ? "User canceled!" : (this.xhr.status + " - " + this.xhr.statusText);
    			this.error.call(this, type, msg || msg, this.xhr);
    		}
	    }catch(ex){
	        UTIL.error("Error firing error callback! message: " + ex.message);
	    }
	},
	
	send : function(url, content, method){
		if(!this.ready){
			UTIL.error("The XMLHttpRequest is not ready for use!");
			this._fireError("The XMLHttpRequest is not ready for use!");
		}else{
    		this.ready = false;
    
    		try{
            	this.xhr.onreadystatechange = this._onReadyStateChange
    			this.xhr.open((method || "POST"), url, true);
    			this.xhr.send(content);
    		}catch(e){
    			UTIL.error(e.message);
    			this._fireError(e.message);
    		}
		}
	},
	
	cancel : function(){
	    if(!this.ready){
	        this.ready = true;

	        this.xhr.abort();

	        this._fireError();
	    }
	}
};

var UTIL = {
    devMode : false,

	hitch : function(thisObj, func){
		if((typeof func) == "string"){
			func = thisObj[func];
		}
		
		return function(){
			return func.apply(thisObj, arguments);
		}
	},

	error : function(msg){
		this.log("ERROR: " + msg);
	},
	
	debug : function(msg){
		this.log("DEBUG: " + msg);
	},
	
	log : function(msg){
	    if(!UTIL.devMode) return;

		var div = document.createElement("DIV");
		div.innerHTML = msg;
		div.style.clear = "both";
		document.body.appendChild(div);
	},
	
	createXMLDocument : function(){
		if(window.ActiveXObject){
			//alert('ie');
			return new ActiveXObject("Microsoft.XMLDOM");
		}else{
			//alert('ff');
			xmlDoc=document.implementation.createDocument("", "", null);
			//xmlDoc.async="false";
			return xmlDoc;
		}
	},

	copy2Clipboard : function(txt){
    	if(window.clipboardData){
    		window.clipboardData.clearData();
    		window.clipboardData.setData("Text",txt);
    		return true;
    	}
    	else if(navigator.userAgent.indexOf("Opera")!=-1){
    		window.location=txt;
    		return true;
    	}
    	else if(window.netscape){
    		try{
    			netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
    		}
    		catch(e){
    			//alert("Clipboard was disabled by your browser, please set "signed.applets.codebase_principal_support" to "true" by enter "about:config" in url.");
    			return false;
    		}
    		var clip=Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(Components.interfaces.nsIClipboard);
    		if(!clip)return;
    		var trans=Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable);
    		if(!trans)return;
    		trans.addDataFlavor('text/unicode');
    		var str=new Object();
    		var len=new Object();
    		var str=Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
    		var copytext=txt;str.data=copytext;
    		trans.setTransferData("text/unicode",str,copytext.length*2);
    		var clipid=Components.interfaces.nsIClipboard;
    		if(!clip)return false;
    		clip.setData(trans,null,clipid.kGlobalClipboard);
    		return true;
    	}
	}
};

var SQLFMT = {
    _VALUE_NAMES : ["clientid", "dbvendor", "outputfmt", "inputsql"],
    _OPTION_NAMES : ["keywordcs", "identifiercs", "functioncs", "lnbrwithcomma", "liststyle","salign", "andorunderwhere", "removelnbr", "quotechar", "trimquotechar", "compactmode", "maxlenincm"],
    _RESULT_NAMES : ["retvalue", "retmessage", "formattedsql"],

    _lastStartTime : 0,
    _lastFormat : "",

    ajax : new AJAX({
    	success : function(type, data, xhr){
    		var response = SQLFMT.parseResponse(data);
    		
    		if(response.retvalue == "0"){
    			//alert(response.formattedsql.substr(-1,50));
    			SQLFMT.view(response.formattedsql);
    			//document.getElementById("timestamp").innerHTML = ((new Date().getTime() - SQLFMT._lastStartTime) / 1000);
    		}else{
    		    if(UTIL.devMode){
        			alert("Processing ended with error!\n" + "Code: " + response.retvalue + "\n" + "Message: " + response.retmessage);
    		    }
    			SQLFMT.view(response.retmessage, true);
    		}

            var btn = document.getElementById("btnformat");
            btn.value = "Format SQL";
    		btn.processing = false;

    		ProcessBar.hide();
    	},
    	error : function(type, msg, xhr){
    	    if(type != "cancel" && UTIL.devMode){
        		alert("System Error: " + msg);
        		SQLFMT.view(msg, true);
    	    }

    		var btn = document.getElementById("btnformat");
    		btn.value = "Format SQL";
    		btn.processing = false;

    		ProcessBar.hide();
    	},
    	onReadyStateChange : function(readyState, msg, xhr){
    		UTIL.debug(readyState + " : " + msg);
    	}
    }),

    format : function(btn){
        var btn = document.getElementById("btnformat");

        if(btn.processing){
            this.ajax.cancel();
        }else{
            var xml = SQLFMT.getArgs();
            
            if(UTIL.devMode){
                var xmlText = document.all ? xml.xml : new XMLSerializer().serializeToString(xml);
                alert("Content sent£º\n" + xmlText);
            }

        	this.ajax.send(document.getElementById("SqlFmtForm").action, xml);

            btn.processing = true;
            btn.value = "Cancel";
        	ProcessBar.show();
        }
    },
    
    view : function(content, hasError){

    	var doc = document.getElementById("ioutputsql").contentWindow.document;

    	doc.designMode = "on";
    	doc.contentEditable = true;

    	
    	if(!doc.body){
    		doc.open();
    		doc.write("<html><head></head><body style='margin:1px; font-size:10pt; white-space:nowrap'></body></html>");
    		doc.close();
    	}
    
    	doc.body.innerHTML = "";

  	    document.getElementById("errorhint").style.display = "none";

		document.getElementById("outputsql").value = content;
    	if(!hasError && SQLFMT._lastFormat.toUpperCase().indexOf("HTML") >= 0){
    	    doc._plainText = false;
    		doc.body.innerHTML = content;
    	    document.getElementById("sp_outputsql").style.display = "inline";
    	}else{
    	    doc._plainText = true;
    	    var pre = doc.createElement("PRE");
    	    doc.body.appendChild(pre);
            if(document.all){
                pre.innerText = content;
            }else{
                pre.textContent = content;
            }
            if (hasError){
    	      document.getElementById("errorhint").style.display = "inline";
    	    }
    	    document.getElementById("sp_outputsql").style.display = "none";
    	}
    },

	getArgs : function(){
		var doc = UTIL.createXMLDocument();

		var sqlpp_request = doc.createElement("sqlpp_request");
		doc.appendChild(sqlpp_request);
		var values = this._readForm(this._VALUE_NAMES);
		this._fullfillNode(doc, sqlpp_request, values);

	    SQLFMT._lastStartTime = new Date().getTime();
	    SQLFMT._lastFormat = values.outputfmt;

        var formatoptions = doc.createElement("formatoptions");
        sqlpp_request.appendChild(formatoptions);
        var options = this._readForm(this._OPTION_NAMES);
        this._fullfillNode(doc, formatoptions, options);

		return doc;
	},

	_readForm : function(names, values){
	    values = values || {};
	    
	    for(var i = 0; i < names.length; ++i){
	        var elms = document.getElementsByName(names[i]);
	        for(var j = 0; j < elms.length; ++j){
	            var elm = elms[j];
	            if((elm.type != "radio" && elm.type != "checkbox") || elm.checked){
	                values[elm.name] = elm.value
	            }
	       }
	    }

	    return values;
	},

	_fullfillNode : function(doc, node, values){
	    for(var k in values){
	        var elm = doc.createElement(k);
	        node.appendChild(elm);
	        elm.appendChild(doc.createTextNode(values[k]));
	    }
	},
	
	parseResponse : function(text){
	    if(UTIL.devMode){
    	    alert("Content received:\n" + text);
	    }


	    var response = {};
	    
	    try{
	        var doc;

	        if(window.DOMParser){
	            doc = new DOMParser().parseFromString(text, "text/xml");
	            doc.normalize(); //The DOMParser truncates data to 4096 characters,this is bug 194231. You can get around it by calling document.normalize()
	            // document.implementation.createDocument("", "", null)
        	   // alert("ff2 "+text.length);
	        }else{
	        	
                doc = UTIL.createXMLDocument();
        	    doc.loadXML(text);
        	    //alert("ff "+text.length);
	        }


    	    for(var i = 0; i < this._RESULT_NAMES.length; ++i){
    	        var rName = this._RESULT_NAMES[i];
    	        try{
        	        //alert(rName+" LENGTH "+doc.firstChild.getElementsByTagName(rName)[0].firstChild.nodeValue.length);
        	        response[rName] = doc.firstChild.getElementsByTagName(rName)[0].firstChild.nodeValue;
    	        }catch(e){
    	            UTIL.error("Error parsing " + rName + " from response! message: " + e.message);
    	        }    	   
    	         }
	    }catch(ex){
            UTIL.error("Error loading XML text: " + text + "! message: " + ex.message);
	    }
	    
	    return response;
	},
	
	copy1 : function(){
	    var doc = document.getElementById("ioutputsql").contentWindow.document;
	    var body = doc.body;

	    var text = "";
	    try{
            if(doc._plainText){
	            text = document.getElementById("outputsql").value;
	        }else{
    	        text = body.innerText || body.textContent;
	        }
	    }catch(e){}

	    text = text.replace(/\xA0/g, String.fromCharCode(32));

	    document.getElementsByName("inputsql")[0].value = text;
	},
	
	copy2 : function(){
	    var doc = document.getElementById("ioutputsql").contentWindow.document;
	    var body = doc.body;

	    var text;
	    try{
	        if(doc._plainText){
	            text = document.getElementById("outputsql").value;
	        }else{
    	        text = body.innerText || body.textContent;
	        }
	    }catch(e){}

	    text = text.replace(/\xA0/g, String.fromCharCode(32));

    	if(UTIL.copy2Clipboard(text.indexOf("\n") >= 0 && text.indexOf("\r\n") < 0 ? text.replace(/\n/g, "\r\n") : text)){
    	    alert("Copy Successfully!");
    	}
	},
	
	copyhtmlcode : function(){

	    var text;
	    try{
    	   text = document.getElementById("outputsql").value;
	    }catch(e){}

	    text = text.replace(/\xA0/g, String.fromCharCode(32));

    	if(UTIL.copy2Clipboard(text.indexOf("\n") >= 0 && text.indexOf("\r\n") < 0 ? text.replace(/\n/g, "\r\n") : text)){
    	    alert("Copy Successfully!");
    	}
	}
	
};

var ProcessBar = {
    _domNode : null,
    
    _create : function(){
        if(this._domNode) return;

        this._domNode = document.createElement("IMG");
        this._domNode.src = "loading.gif";
        this._domNode.style.width = "48px";
        this._domNode.style.height = "48px";
        this._domNode.style.position = "absolute";
        this._domNode.style.cursor = "pointer";
        this._domNode.style.display = "none";
        this._domNode.title = "Click to cancel!";
        this._domNode.onclick = function(){ SQLFMT.ajax.cancel(); };
        document.body.appendChild(this._domNode);
    },

    show : function(){
        this._create();

        this._domNode.style.left = Math.max((document.documentElement.clientWidth - this._domNode.clientWidth) / 2, 0) + document.documentElement.scrollLeft + "px";
        this._domNode.style.top = Math.max((document.documentElement.clientHeight - this._domNode.clientHeight) / 2, 0) + document.documentElement.scrollTop + "px";
        this._domNode.style.display = "block";
    },
    
    hide : function(){
        if(this._domNode){
            this._domNode.style.display = "none";
        }
    }
};
