// A generic function for performming AJAX requests
// It takes one argument, which is an object that contains a set of options
// All of which are outline in the comments, below
function ajax( options ) {

    // Load the options object with defaults, if no
    // values were provided by the user
    options = {
        // The type of HTTP Request
        type: options.type || "POST",

        // The URL the request will be made to
        url: options.url || "",

        // How long to wait before considering the request to be a timeout
        timeout: options.timeout || 5000,

        // Functions to call when the request fails, succeeds,
        // or completes (either fail or succeed)
        onComplete: options.onComplete || function(){},
        onError: options.onError || function(){},
        onSuccess: options.onSuccess || function(){},

        // The data type that'll be returned from the server
        // the default is simply to determine what data was returned from the
        // and act accordingly.
        data: options.data || ""
    };

    // Create the request object
    var xml = (document.all) ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();

    // Open the asynchronous POST request
    xml.open(options.type, options.url, true);

    // We're going to wait for a request for 5 seconds, before giving up
    var timeoutLength = 5000;

    // Keep track of when the request has been succesfully completed
    var requestDone = false;

    // Initalize a callback which will fire 5 seconds from now, cancelling
    // the request (if it has not already occurred).
    setTimeout(function(){
         requestDone = true;
    }, timeoutLength);

    // Watch for when the state of the document gets updated
    xml.onreadystatechange = function(){
        // Wait until the data is fully loaded,
        // and make sure that the request hasn't already timed out
        if ( xml.readyState == 4 && !requestDone ) {

            // Check to see if the request was successful
            if ( httpSuccess( xml ) ) {

                // Execute the success callback with the data returned from the server
                options.onSuccess( httpData( xml, options.type ) );

            // Otherwise, an error occurred, so execute the error callback
            } else {
                options.onError();
            }

            // Call the completion callback
            options.onComplete();

            // Clean up after ourselves, to avoid memory leaks
            xml = null;
        }
    };

    // Establish the connection to the server
    xml.send();

    // Determine the success of the HTTP response
    function httpSuccess(r) {
        try {
            // If no server status is provided, and we're actually 
            // requesting a local file, then it was successful
            return !r.status && location.protocol == "file:" ||

                // Any status in the 200 range is good
                ( r.status >= 200 && r.status < 300 ) ||

                // Successful if the document has not been modified
                r.status == 304 ||

                // Safari returns an empty status if the file has not been modified
                navigator.userAgent.indexOf("Safari") >= 0 && typeof r.status == "undefined";
        } catch(e){}

        // If checking the status failed, then assume that the request failed too
        return false;
    }

    // Extract the correct data from the HTTP response
    function httpData(r,type) {
        // Get the content-type header
        var ct = r.getResponseHeader("content-type");

        // If no default type was provided, determine if some
        // form of XML was returned from the server
        var data = !type && ct && ct.indexOf("xml") >= 0;

        // Get the XML Document object if XML was returned from
        // the server, otherwise return the text contents returned by the server
        data = type == "xml" || data ? r.responseXML : r.responseText;
        // If the specified type is "script", execute the returned text
        // response as if it was JavaScript
        if ( type == "script" )
            eval.call( window, data );

        // Return the response data (either an XML Document or a text string)
        return data;
    }
}

// communication utils
if (!argos) var argos = {};

argos.comms = {
	
	/**
	*
	* URL encode / decode
	* http://www.webtoolkit.info/
	*
	**/
	
	url : {
		URI_SEP : "?",
		PARAM_DELIM : "&",
		PARAM_ASSIGN_CHAR : "=",
		ANCHOR_CHAR : "#",
	
	    // public method for url encoding
	    encode : function (string) {
	        return escape(this._utf8_encode(string));
	    },
	
	    // public method for url decoding
	    decode : function (string) {
	        return this._utf8_decode(unescape(string));
	    },
	
	    // private method for UTF-8 encoding
	    _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;
	    },
	
	    // private method for UTF-8 decoding
	    _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;
	    },
	
		/**
		 * Parse an URL string and get the value for a given parameter
		 * @url the URL to be parsed
		 * @name the parameter name
		 */
		getParameter : function(url, name) {
			var value = null; // parameter value
			
			// validate input url and name and if url has parameters before parsing
			if (url && name && url.indexOf(argos.comms.url.URI_SEP)!=-1) {			
				var paramStr = url.substring(url.indexOf(argos.comms.url.URI_SEP)); 
				var paramIndex = paramStr.indexOf(name);
				
				if (paramIndex!=-1) { // parameter found
					var valueStart = paramIndex + name.length + argos.comms.url.PARAM_ASSIGN_CHAR.length;
					var valueEnd = paramStr.indexOf(argos.comms.url.PARAM_DELIM, valueStart);

					if (valueEnd == -1) {	// parameter is the last one
						valueEnd = paramStr.indexOf(argos.comms.url.ANCHOR_CHAR, valueStart);
						if (valueEnd == -1) { // check for anchor maker
							valueEnd = paramStr.length;
						}
					}
					
					value = paramStr.substring(valueStart, valueEnd);
				}
			}
			
			return value;			
		}
	}

};