/*
 * Ext Core Library 3.0
 * http://extjs.com/
 * Copyright(c) 2006-2009, Ext JS, LLC.
 * 
 * The MIT License
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 */

/*
 * Portions of this file are based on pieces of Yahoo User Interface Library
 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
 * YUI licensed under the BSD License:
 * http://developer.yahoo.net/yui/license.txt
 */
    Ext.lib.Ajax = function() {	    
	    var activeX = ['MSXML2.XMLHTTP.3.0',
			           'MSXML2.XMLHTTP',
			           'Microsoft.XMLHTTP'];
			           
		// private
		function setHeader(o) {
	        var conn = o.conn,
	        	prop;
	        
	        function setTheHeaders(conn, headers){
		     	for (prop in headers) {
                    if (headers.hasOwnProperty(prop)) {
                        conn.setRequestHeader(prop, headers[prop]);
                    }
                }   
	        }		
	        
            if (pub.defaultHeaders) {
	            setTheHeaders(conn, pub.defaultHeaders);
            }

            if (pub.headers) {
				setTheHeaders(conn, pub.headers);
                pub.headers = null;                
            }
        }    
        
        // private
        function createExceptionObject(tId, callbackArg, isAbort, isTimeout) {	        
            return {
	            tId : tId,
	            status : isAbort ? -1 : 0,
	            statusText : isAbort ? 'transaction aborted' : 'communication failure',
                    isAbort: true,
                    isTimeout: true,
	            argument : callbackArg
            };
        }  
        
        // private 
        function initHeader(label, value) {         
			(pub.headers = pub.headers || {})[label] = value;			            
        }
	    
        // private
        function createResponseObject(o, callbackArg) {
            var headerObj = {},
            	headerStr,            	
            	conn = o.conn;            	

            try {
                headerStr = o.conn.getAllResponseHeaders();                
                Ext.each(headerStr.split('\n'), function(v){
	            	var t = v.indexOf(':');
                    headerObj[v.substr(0, t)] = v.substr(t + 1);
                });
            } catch(e) {}
                        
            return {
		        tId : o.tId,
	            status : conn.status,
	            statusText : conn.statusText,
	            getResponseHeader : function(header){return headerObj[header];},
                getAllResponseHeaders : function(){return headerStr},
	            responseText : conn.responseText,
	            responseXML : conn.responseXML,
	            argument : callbackArg
        	};
        }
        
        // private
        function releaseObject(o) {
            o.conn = null;
            o = null;
        }        
	    
        // private
        function handleTransactionResponse(o, callback, isAbort, isTimeout) {
            if (!callback) {
                releaseObject(o);
                return;
            }

            var httpStatus, responseObject;

            try {
                if (o.conn.status !== undefined && o.conn.status != 0) {
                    httpStatus = o.conn.status;
                }
                else {
                    httpStatus = 13030;
                }
            }
            catch(e) {
                httpStatus = 13030;
            }

            if ((httpStatus >= 200 && httpStatus < 300) || (Ext.isIE && httpStatus == 1223)) {
                responseObject = createResponseObject(o, callback.argument);
                if (callback.success) {
                    if (!callback.scope) {
                        callback.success(responseObject);
                    }
                    else {
                        callback.success.apply(callback.scope, [responseObject]);
                    }
                }
            }
            else {
                switch (httpStatus) {
                    case 12002:
                    case 12029:
                    case 12030:
                    case 12031:
                    case 12152:
                    case 13030:
                        responseObject = createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false), isTimeout);
                        if (callback.failure) {
                            if (!callback.scope) {
                                callback.failure(responseObject);
                            }
                            else {
                                callback.failure.apply(callback.scope, [responseObject]);
                            }
                        }
                        break;
                    default:
                        responseObject = createResponseObject(o, callback.argument);
                        if (callback.failure) {
                            if (!callback.scope) {
                                callback.failure(responseObject);
                            }
                            else {
                                callback.failure.apply(callback.scope, [responseObject]);
                            }
                        }
                }
            }

            releaseObject(o);
            responseObject = null;
        }  
        
        // private
        function handleReadyState(o, callback){
	    callback = callback || {};
            var conn = o.conn,
            	tId = o.tId,
            	poll = pub.poll,
		cbTimeout = callback.timeout || null;

            if (cbTimeout) {
                pub.timeout[tId] = setTimeout(function() {
                    pub.abort(o, callback, true);
                }, cbTimeout);
            }

            poll[tId] = setInterval(
                function() {
                    if (conn && conn.readyState == 4) {
                        clearInterval(poll[tId]);
                        poll[tId] = null;

                        if (cbTimeout) {
                            clearTimeout(pub.timeout[tId]);
                            pub.timeout[tId] = null;
                        }

                        handleTransactionResponse(o, callback);
                    }
                },
                pub.pollInterval);
        }
        
        // private
        function asyncRequest(method, uri, callback, postData) {
            var o = getConnectionObject() || null;

            if (o) {
                o.conn.open(method, uri, true);

                if (pub.useDefaultXhrHeader) {                    
                	initHeader('X-Requested-With', pub.defaultXhrHeader);
                }

                if(postData && pub.useDefaultHeader && (!pub.headers || !pub.headers['Content-Type'])){
                    initHeader('Content-Type', pub.defaultPostHeader);
                }

                if (pub.defaultHeaders || pub.headers) {
                    setHeader(o);
                }

                handleReadyState(o, callback);
                o.conn.send(postData || null);
            }
            return o;
        }
        
        // private
        function getConnectionObject() {
            var o;      	

            try {
                if (o = createXhrObject(pub.transactionId)) {
                    pub.transactionId++;
                }
            } catch(e) {
            } finally {
                return o;
            }
        }
	       
        // private
        function createXhrObject(transactionId) {
            var http;
            	
            try {
                http = new XMLHttpRequest();                
            } catch(e) {
                for (var i = 0; i < activeX.length; ++i) {	            
                    try {
                        http = new ActiveXObject(activeX[i]);                        
                        break;
                    } catch(e) {}
                }
            } finally {
                return {conn : http, tId : transactionId};
            }
        }
	         
	    var pub = {
	        request : function(method, uri, cb, data, options) {
			    if(options){
			        var me = this,		        
			        	xmlData = options.xmlData,
			        	jsonData = options.jsonData;
			        	
			        Ext.applyIf(me, options);	        
		            
		            if(xmlData || jsonData){
			            initHeader('Content-Type', xmlData ? 'text/xml' : 'application/json');
			            data = xmlData || (Ext.isObject(jsonData) ? Ext.encode(jsonData) : jsonData);
			        }
			    }		    		    
			    return asyncRequest(method || options.method || "POST", uri, cb, data);
	        },
	
	        serializeForm : function(form) {
		        var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
	            	hasSubmit = false,
	            	encoder = encodeURIComponent,
		        	element,
	            	options, 
	            	name, 
	            	val,             	
	            	data = '',
	            	type;
	            	
		        Ext.each(fElements, function(element) {		            
	                name = element.name;	             
					type = element.type;
					
	                if (!element.disabled && name){
		                if(/select-(one|multiple)/i.test(type)){			                
				            Ext.each(element.options, function(opt) {
					            if (opt.selected) {
						            data += String.format("{0}={1}&", 						            					  
						            					 encoder(name),						            					 
						            					  (opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttribute('value') !== null) ? opt.value : opt.text);
                                }								
                            });
		                } else if(!/file|undefined|reset|button/i.test(type)) {
			                if(!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)){
                                    
                                data += encoder(name) + '=' + encoder(element.value) + '&';                     
                                hasSubmit = /submit/i.test(type);    
                            } 		                
		                } 
	                }
	            });            
	            return data.substr(0, data.length - 1);
	        },
	        
	        useDefaultHeader : true,
	        defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
	        useDefaultXhrHeader : true,
	        defaultXhrHeader : 'XMLHttpRequest',        
	        poll : {},
	        timeout : {},
	        pollInterval : 50,
	        transactionId : 0,
	        
//	This is never called - Is it worth exposing this?  		        
// 	        setProgId : function(id) {
// 	            activeX.unshift(id);
// 	        },

//	This is never called - Is it worth exposing this?  	
// 	        setDefaultPostHeader : function(b) {
// 	            this.useDefaultHeader = b;
// 	        },
	        
//	This is never called - Is it worth exposing this?  	
// 	        setDefaultXhrHeader : function(b) {
// 	            this.useDefaultXhrHeader = b;
// 	        },

//	This is never called - Is it worth exposing this?        	
// 	        setPollingInterval : function(i) {
// 	            if (typeof i == 'number' && isFinite(i)) {
// 	                this.pollInterval = i;
// 	            }
// 	        },
	        
//	This is never called - Is it worth exposing this?
// 	        resetDefaultHeaders : function() {
// 	            this.defaultHeaders = null;
// 	        },
	
	        abort : function(o, callback, isTimeout) {
		        var me = this,
		        	tId = o.tId,
		        	isAbort = false;
		        
	            if (me.isCallInProgress(o)) {
	                o.conn.abort();
	                clearInterval(me.poll[tId]);
	               	me.poll[tId] = null;
	                if (isTimeout) {
	                    me.timeout[tId] = null;
	                }
					
	                handleTransactionResponse(o, callback, (isAbort = true), isTimeout);                
	            }
	            return isAbort;
	        },
	
	        isCallInProgress : function(o) {
	            // if there is a connection and readyState is not 0 or 4
	            return o.conn && !{0:true,4:true}[o.conn.readyState];	        
	        }
	    };
	    return pub;
    }();