// AJAX functionality
ajax_logging = false;

// ajax constructor
function ajax() {

// Private methods
function createXMLHttpRequest() {
  // Creates a browser-sensitive request object.
	
	var throwexception = false;
	
	// Attempt to create object, throw exception if can't
	try {
		req = new XMLHttpRequest();
	} catch (e) {
	  throwexception = true;
	};
	
	if (throwexception) {
	  try {
		  req = new ActiveXObject('Msxml2.XMLHTTP');
		  throwexception = false;
	  } catch (e) {
	    throwexception = true;
	  };
	};
	
	if (throwexception) {
	  try {
		  req = new ActiveXObject('Microsoft.XMLHTTP');
		  throwexception = false;
	  } catch (e) {
	    throwexception = true;
	  };
	};

	if (throwexception) {
		throw new AJAXException("createXMLHttpRequest","Could not create an XMLHttpRequest object.");
	};
	
	return(req);
};

function AJAXException(name,message) {	
	
	this.name = name;
	this.message = message;
  this.ajaxexception = true;
  	
	// these are populated in errorXMLHttpRequest
	this.readyState = "";
	this.status = "";
	this.statusText = "";
	this.headers = "";
	this.responseText = "";
};

function errorXMLHttpRequest(ajaxExcept) {
  
  // fill in request details if req existed.
	if (req != null) {
		ajaxExcept.readyState = req.readyState;
		
		// Do not handle headers, status or statustext unless readystate is complete
		if (req.readyState == 4) {
		  ajaxExcept.headers = req.getAllResponseHeaders();
			ajaxExcept.status = req.status;
			ajaxExcept.statusText = req.statusText;
			ajaxExcept.responseText = req.responseText;
		};
		
	};
  
  logXMLHttpRequest("AJAX Exception: " + ajaxExcept.name + ": " + ajaxExcept.message,that.id);
  
  // call user error function if specified.
	//if (notify_error) notify_error(ajaxExcept);

  // Abort and "Nullify" req object
	//req.abort();
	//req = null;

	return(ajaxExcept);

};

function logXMLHttpRequest(msg,id,req,method,url,params) {

	if (document.logXMLHttpRequestWindow) {
			document.logXMLHttpRequestWindow.add(msg,id,req,method,url,params);
	};
};

function AJAXLogWindow() {
	
	var that = this;
	if (!document.logwindow) {
	  document.logwindow = window.open('','winLog','scrollbars=yes,resizable=yes,width=1000,height=300,status=no');
    };
  	
	var responses = new Array();
	var content_types = new Array();
	
	this.add = function (msg,id,req,method,url,params) { 
		
		if ((!document.logwindow) || (document.logwindow.document == null)){
			return;
		};
		
		var out = '';
		var count = 0;
		var headings = '';
		var sections = '';
		var params_str = '';
		
		if (method == 'POST') {
		  if (params != undefined) {
		    params_str = params;
		  };
		} else if (method == 'GET') {
		  if (url != undefined) {
		    params_str = url.split("?")[1];
		    url = url.split("?")[0];
		  };
		};
		
		if ((params_str != '') && (params_str != undefined)) {
	      count++;			
		  headings += '&nbsp;<input type="button" value="Params" name="button_"' + String(id) + '_params" onClick="javascript:var div = document.getElementById(\'' + String(id) + '_params\'); var state = div.style.display; div.style.display = (state == \'none\') ? \'block\' : \'none\';">&nbsp;';
		  sections += '<div id="' + id + '_params" style="display:none;">';
			
		  // Extract params from query string 
		  var params_hash = new Object();
     	  var pairs = params_str.split("&");
          for (i = 0; i < pairs.length; i++){
            var pair = pairs[i].split("=");
            if (pair.length = 2) {
        	  params_hash[ pair[0] ] = pair[1];
            } else {
        	  params_hash[ pair[0] ] = '';
            };
          };
      
          // Print params
          var out = '';
          for (key in params_hash) {
            out += key + ': ' + params_hash[key] + '<BR/>';
          }
  			
		   sections += '<pre>' + out + '</pre></div>';
		};
		
		if (req != undefined) {
		   
		  if ((req.readyState == 4) && (req.responseText !=undefined)) {
		    count++;			
  		    headings += '&nbsp;<input type="button" value="Response" name="button_"' + String(id) + '_response" onClick="javascript:var div = document.getElementById(\'' + String(id) + '_response\'); var state = div.style.display; div.style.display = (state == \'none\') ? \'block\' : \'none\';">&nbsp;';
	  		sections += '<div id="' + id + '_response" style="display:none;">';
			var div = document.createElement('div');
            var text = document.createTextNode(req.responseText);
            div.appendChild(text);
            sections += '<pre>' + div.innerHTML + '</pre></div>';
		  };
  		
		  if ((req.readyState == 4) && (req.getAllResponseHeaders != undefined) && (req.getAllResponseHeaders() != '')) {
  		
		    count++;			
  		    headings += '&nbsp;<input type="button" value="Headers" name="button_"' + String(id) + '_headers" onClick="javascript:var div = document.getElementById(\'' + String(id) + '_headers\'); var state = div.style.display; div.style.display = (state == \'none\') ? \'block\' : \'none\';">&nbsp;';
	  		sections += '<div id="' + id + '_headers" style="display:none;">';
			var rh = req.getAllResponseHeaders();
			var search = "\n";
			var replace = "<BR/>";
			var re = new RegExp(search,"g");
			var rh = rh.replace(re,replace);
            sections += '<pre>' + rh + '</pre></div>';
		  };
		};
		
		
		if ((id != undefined) && (id != '')) {
		  msg = id + ': ' + msg;
		};
		
		if ((method != undefined) && (method != '')) {
		  msg += ': '+method;
		};
		
		if ((url != undefined) && (url != '')) {
		  msg += ': '+url;
		};
		
		if (count > 0) {
		  msg += headings;
		};
		 
		out = '<pre>' + msg + '</pre>'+ "\n";
		
		if (count > 0) {
		  out += sections;
		};
		
		if (document.logwindow.document != null) {
			document.logwindow.document.writeln(out);
		};
		
  	};
	
	
};


function validateXMLHttpRequest(method,url,asynch,content_type,notify_success,notify_error,params) {

    var error = '';
    
    url = url.split("?")[0];
    
    // TODO - validate / constrain mime types
    if ((method != 'GET') && (method != 'POST')) {
      error = url + ": " + "Method must be POST or GET.";
      
    } else if ((notify_success != '') && (typeof notify_success != 'function')) {
		  error = method + ": " + url + ": notify_success must either be an empty string or a function.";
		  
		} else if ((notify_error != '') && (typeof notify_error != 'function')) {
		  error = method + ": " + url + ": notify_error must either be an empty string or a function.";
		  
		} else if ((method == 'GET') && (params != '')) {
		  error = "GET: " + url + ": params field must be used for POST requests only.";
		
		} else if ((method == 'POST') && (params == '')) {
		  error = "POST: " + url + ": no params supplied for POST.";
		
		} else if (content_type == '') {
		  error = method + ": " + url + ": content-type must be specified.";
		  
		};
		
		if (error != '') {
		  throw new AJAXException("validateXMLHttpRequest",error);
    };
    
    return(true);
};


// Privileged method
this.performXMLHttpRequest = function (
							   method,				  // GET or POST usually
							   url,					    // URL of web service / script
							   asynch,				  // true = asynchronous (code keeps executing)
							   						      // false = synchronous (code stops executing until request finished)
							   content_type,		// content (mime) type of request
							   notify_success,	// function to call on receipt of server response
							   notify_error,		// function to call in case of exception (client or server)					   						      
							   params			      // params for a POST request (as a GETlike querystring)
							   ) {

	try {

	  throwclientexception = false;    
	  ajaxExcept = null;
	  var marker = '';

      // validate parameters (throws exception if not valid)
      validateXMLHttpRequest(method,url,asynch,content_type,notify_success,notify_error,params);
		
	  // Track readyState variable.
	  // 0: uninitialized
	  // 1: loading
	  // 2: loaded
	  // 3: interactive
	  // 4: complete
	  req.onreadystatechange = function () {
		
	    // "this" refers to req in inline function	
		if (req.readyState == 4) {
			  logXMLHttpRequest("AJAX Response: " + req.status + ": " + req.statusText,that.id,req);
		
			  // Track server status
			  // 200: OK (Success)
			  // Otherwise Error
			  if (req.status == 200) {
				if (notify_success) notify_success(req);
			  } else {
				if (notify_error) notify_error(req);
			  };
			
		 };
		  
	  };
		
	  marker = 'open';
      req.open(method,url,asynch);
			
      // Request must be open to set header info
	  marker = 'setRequestHeader';
			
			
	  if (content_type != "") {
		  req.setRequestHeader('Content-Type', content_type);
	  } else  {
		  req.setRequestHeader('Content-Type', 'text/plain');
	  };
			
	  marker = 'log request';
	  logXMLHttpRequest('AJAX Request',that.id,req,method,url,params)
		
	  // Send request
	  marker = 'send';
	  if ((params != "") && (method == "POST")) {
			req.send(params);
	  } else {
			req.send(null);
	  };
    		
	} catch (e) {
				
		// This should catch any client errors
		// - when creating XMLHttpRequest object.
		// - when opening / sending / receiving request
		// - other client errors
		// Server errors are trapped in onreadystatechange above
		
	  //alert("ajax exception");
	  ajaxExcept = errorXMLHttpRequest( new AJAXException((marker ? marker + ': ' : '') + e.name, e.message));
		
		// Throw client errors again.
		throwclientexception = true;	
		
	};
	
	if (throwclientexception) {
	  throw (ajaxExcept);
	} else {
	  return(true);
	};
};

// *******************************************************
// ajax initialization

var req;
var ajaxExcept;
var throwclientexception = false;

// Create unique ajax id
this.id=Math.round(Math.random()*1000000);

// Needed to pass "this" to private methods.
var that = this;

// Enable window logging if ajax constructed with "true".
if (ajax_logging) {    
  document.logXMLHttpRequestWindow = new AJAXLogWindow();
} else {
  document.logXMLHttpRequestWindow = null;
};

// Create request object
try {
  req = createXMLHttpRequest();
} catch (e) {
  ajaxExcept = errorXMLHttpRequest( new AJAXException(e.name, e.message));
  throwclientexception = true;
};

if (throwclientexception) {
	  throw (ajaxExcept);
};


}; // End ajax constructor

