/* Ajax Library
 *
 * Nima Khoshini
 * Last Updated: April 3, 2006
 *
 */

/**
 * 
 * Ajax Object
 * Used to create a remote scripting object to fetch data asyncronously or synchronously
 *
 * Properties:
 *    WebURL - The URL to connect. This property is of type 'AjaxURL'.
 *    Callback - Specifies a function that will get called once a connection has been opened an a result has been received
 *    ReturnedResults - The complete data returned from the call.
 *    ResultSet - An object of type AjaxResultSet that contains the returned dataset
 */

/*
 * Constructor
 */
var AjaxObject = function(url)
{
    this.WebURL          = new AjaxURL(url);
    this.Callback        = null;
    this.ReturnedResults = null;
    this.ErrorCode		 = null;
    
    this.ResultSet       = null;
}

/*
 * Open() - Opens the connection to the URL. 
 * If the Callback Property is defined, the underlying protocol will call the Callback function
 * once a result is received.
 */
AjaxObject.prototype.Open = function()
{
	if (null == this.XMLHttp)
		this.XMLHttp = this.GetXmlHttp();
	
	this.WebURL.AddRandom();
	
	if (null != this.Callback)
	{
		var rand = Math.random();
		while (null != ASyncObjects[rand])
			rand = Math.random();
			
		ASyncObjects[rand] = this;
			
		this.XMLHttp.onreadystatechange = function()
		{
			var ajaxObject = ASyncObjects[rand];
			if (null == ajaxObject)
				return;
				
			if (ajaxObject.XMLHttp.readyState == 4 && null != ajaxObject.Callback)
			{
				ajaxObject.ReturnedResults = ajaxObject.XMLHttp.responseText;
				ajaxObject.PrepareResults(ajaxObject.XMLHttp.responseText);
				ajaxObject.Callback(ajaxObject);
			}
		}
		
		this.XMLHttp.open("GET", this.WebURL.FullURL, true);
		this.SendXML();
	}
	else
	{
		this.XMLHttp.open("GET", this.WebURL.FullURL, false);
		this.SendXML();
		
		this.ReturnedResults = this.XMLHttp.responseText;
		this.PrepareResults(this.XMLHttp.responseText);
	}
}

/*
 * GetXmlHttp() - Private - Gets the XmlHttp object for connection
 */
AjaxObject.prototype.GetXmlHttp = function()
{
	var ret = null;
	if (window.XMLHttpRequest)
    {
    	try
    	{
			ret = new XMLHttpRequest();
        }
        catch(e)
        {
          	ret = null;
        }
    }
    else if (window.ActiveXObject)
    {
        try
        {
            ret = new ActiveXObject("Msxml2.XMLHTTP");
      	}
        catch(e)
        {
            try
            {
          		ret = new ActiveXObject("Microsoft.XMLHTTP");
        	}
            catch(e)
            {
          		ret = null;
        	}
		}
    }
    
	return ret;
}

/*
 * Close() - Closes and de-allocates the Ajax Object 
 */
AjaxObject.prototype.Close = function()
{ 
	if (null != this.XMLHttp)
		this.XMLHttp = null;
}

/*
 * SendXML() - Private - Sends the command
 */
AjaxObject.prototype.SendXML = function()
{ 
	if (document.all)
		this.XMLHttp.send();
	else
		this.XMLHttp.send(null);
}

/*
 * HasResults() - Determines whether the call returned valid data.
 * Returns - True - If the call returned valid data. False otherwise
 */
AjaxObject.prototype.HasResults = function()
{
	return null != this.ReturnedResults && null != this.ResultSet && this.ResultSet.HasResults();
}

/*
 * HasResults() - Determines whether the call returned valid data.
 * Returns - True - If the call returned valid data. False otherwise
 */
AjaxObject.prototype.HasError = function()
{
	return false == this.HasResults() && false == IsStringEmpty(this.ErrorCode);
}

/*
 * PrepareResults() - Private - Initializes the dataset
 */
AjaxObject.prototype.PrepareResults = function()
{
    if (null == this.ReturnedResults)
        return;
        
    //since we are not using XML at the time, remove the <?xml tag at the beginning of the result set
    var results = this.ReturnedResults;
    
    var xmlTagIndex = results.indexOf("?>");
    if (xmlTagIndex > 0)
    {
        results = results.substring(xmlTagIndex+2);
        this.ResultSet = new AjaxResultSet(results, this);
        /* if we had an error, set the result set to null
         */
         if (null != this.ErrorCode)
         	this.ResultSet = null;
    }
    else
        this.ResultSet = null; 
}

/*
 * Global registry for async objects
 */
var ASyncObjects = new Array();

/**
 * 
 * AjaxResultSet Object
 * Used to retrieve data from an Ajax Object.
 */
var AjaxResultSet = function(setToParse, ajaxObject)
{
    /* initialize the properties
     */
 	this.Parent = ajaxObject;
    this.ResultsArray = new Array();
    this.CachedResultsArray  = new Array();
    this.ResultKeys   = new Array();
    this.ResultValues = new Array();
    this.CurItemIndex = 0;
    this.SortColumn = null;
    
    var delimItem  = "^^";
    var delimItem2 = "~~";
    var delimLine  = "@!";
    
    var res = setToParse.split(delimLine);
	var i=0;
	
	if (res.length > 1 && res[0].indexOf('ERROR'+delimItem) == 0)
	{
		var error = res[0].substring(res[0].indexOf(delimItem) + 2, res[0].indexOf(delimItem2));
	    if (this.Parent)
	    	this.Parent.ErrorCode = error;
	}
	else
	{
		for (i=0; i<res.length; ++i)
		{
		    if (null == res[i] || TrimString(res[i]) == '')
		        continue;
		        
		    this.CachedResultsArray[i] = new Array();
		    var items = res[i].split(delimItem2);
		    
		    var j=0;
		    for (j=0; j<items.length; ++j)
		    {
		        var vals = items[j].split(delimItem);
		        var itemName  = TrimString(vals[0]);
		        var itemValue = (vals.length == 1) ? "" : TrimString(vals[1]);
		        
		        if (0 == i && itemName.length > 0)
		        {
					this.ResultKeys[this.ResultKeys.length++] = itemName;
					this.ResultValues[this.ResultValues.length++] = itemValue;
				}
		        	        
		        this.CachedResultsArray[i][itemName] = itemValue;
		        this.CachedResultsArray[i]["_ResultSetParent"] = this;
		    }
		}
		
		// set the ResultsArray with CachedResultsArray initially
		this.ResultsArray = this.CachedResultsArray;
		this.Count = this.ResultsArray.length;
	}
}

/*
 * SortByValue() - Takes in two parameters: column name and the value to search for
 *                 This function will do a 'like' search on a column.
 * Return - The refined result array
 */ 
AjaxResultSet.prototype.SortByValue = function(column, searchString)
{
    var pattern = new RegExp("^" + searchString, "i");
    var j = 0;
    this.ResultsArray = null;
    this.ResultsArray = new Array();
    for (i = 0; i < this.CachedResultsArray.length; ++i)
    {
        var value = this.CachedResultsArray[i][column];
        if (null != value && "" != value)
        {
            if (pattern.test(value))
                this.ResultsArray[j++] = this.CachedResultsArray[i];
        }
    }
    
    return this.ResultsArray;            
}

/*
 * SortWithExistingData() - Takes in two parameters: column name and the value to search for
 *                 This function will do a 'like' search on a column in an existing datagrid column.
 * Return - The refined result array
 */ 
AjaxResultSet.prototype.SortWithExistingData = function(column, searchString)
{
    var pattern = new RegExp("^" + searchString, "i");
    var j = 0;
    var tempResultsArray = this.ResultsArray;
     
    this.ResultsArray = null;
    this.ResultsArray = new Array();
    for (i = 0; i < tempResultsArray.length; ++i)
    {
        var value = tempResultsArray[i][column];
        if (null != value && "" != value)
        {
            if (pattern.test(value))
                this.ResultsArray[j++] = tempResultsArray[i];
        }
    }
    
    return this.ResultsArray;            
}


/*
 * ShowDefault() - Takes in two parameters: column name and the value to search for
 *                 Reverts back to original datagrid.
 * Return - The refined result array
 */ 
AjaxResultSet.prototype.ShowDefault = function()
{
    this.ResultsArray = this.CachedResultsArray;
    
    return this.ResultsArray;
}

/*
 * HasResults() - Determines whether the call returned valid data.
 * Returns - True - If the call returned valid data. False otherwise
 */
AjaxResultSet.prototype.HasResults = function()
{
	return null != this.ResultsArray && this.ResultsArray.length > 0;
}

/*
 * NextItem() - Advances the result set to the next item. Returns the next item, if existant, in the result set
 */
AjaxResultSet.prototype.NextItem = function()
{
    if (null ==  this.ResultsArray || 0 == this.ResultsArray.count || this.CurItemIndex > this.ResultsArray.count)
        return null;
        
    return this.ResultsArray[this.CurItemIndex++]
}

/*
 * LastItem() - Retrieves teh previous item in the result set.
 */
AjaxResultSet.prototype.LastItem = function()
{
    if (null ==  this.ResultsArray || 0 == this.ResultsArray.count)
        return null;
        
    var ret = this.ResultsArray[this.CurItemIndex];
    if (this.CurItemIndex > 0)
        --this.CurItemIndex;
        
    return ret;
}

/*
 * ItemAt() - Advances the result set to the next item. Returns the next item, if existant, in the result set
 */
AjaxResultSet.prototype.ItemAt = function(position)
{
    if (null ==  this.ResultsArray || 0 == this.ResultsArray.count || position > this.ResultsArray.count)
        return null;
        
    return this.ResultsArray[position];
}
/*
 * ResetPosition() - Resets the position of the result set enumerator.
 */
AjaxResultSet.prototype.ResetPosition = function()
{
    this.CurItemIndex = 0;
}

/*
 * GetKeys() - Gets the keys of the first item returned in the result set. The returned result is an array of strings containing the keys.
 */
AjaxResultSet.prototype.GetKeys = function()
{
    return this.ResultKeys;
}

/*
 * GetValues() - Gets the values of the first item returned in the result set. The returned result is an array containing the values.
 */
AjaxResultSet.prototype.GetValues = function()
{
    return this.ResultValues;
}

/*
 * GetFirstResult() - Returns the first result in the set.
 */
AjaxResultSet.prototype.GetFirstResult = function()
{
    if (null ==  this.ResultsArray || 0 == this.ResultsArray.count)
        return null;
        
    return this.ResultsArray[0];
}

/*
 * GetLastResult() - Returns the last result in the set.
 */
AjaxResultSet.prototype.GetLastResult = function()
{
    if (null ==  this.ResultsArray || 0 == this.ResultsArray.count)
        return null;
        
    return this.ResultsArray[this.ResultsArray.length-1];
}

AjaxResultSet.prototype.Sort = function(column)
{
    if (null ==  this.ResultsArray || 0 == this.ResultsArray.count)
        return null;
        
    this.SortColumn = column;
    this.ResultsArray.sort(ResultsArraySort);
}

function ResultsArraySort(a, b)
{
	var par = a["_ResultSetParent"];
	var col = par.SortColumn;
	
	var compA = String(a[col]);
	var compB = String(b[col]);
	
	compA = compA.toLowerCase();
	compB = compB.toLowerCase();
	
	if (compA == compB)
		return 0;
	else if (compA > compB)
		return 1;
	
	return -1;
}

/**
 * 
 * AjaxURL Object
 * Used to create a URL. AjaxURL manages escaping of characters and proper formatting of the URL string.
 *
 * Properties:
 *    FullURL - The complete URL path.
 */
 
/*
 * Constructor - Creates an AjaxURL object.
 *
 * Paramaters:
 *    url - the initial URL
 *    replacePound - specifies whether or not to trim pound characters found on the end of the URL string (ex: http://localhost/test.aspx#)
 */
var AjaxURL = function(url, replacePound)
{
    this.FullURL = url;
    
    if (true == replacePound)
        this.FullURL = this.FullURL.replace("#", '');
}

/*
 * AddParam(name, val, replaceIfExists) - Adds a paramater or query string to the URL
 *
 * Paramaters:
 *    name - the name of the query string or paramater
 *    val - the value
 *    replaceIfExists - determines whether or not to replace the value of paramater if it already exists in the URL. By default, this paramater is set to false.
 */
AjaxURL.prototype.AddParam = function(name, val, replaceIfExists)
{
    if (null == this.FullURL)
        this.FullURL = "";
        
    if (this.FullURL.length > 0)
    {
        if (this.FullURL.indexOf('?') == -1)
            this.FullURL += "?";
        else
        {
            if (this.FullURL.charAt(this.FullURL.length - 1) != '&')
               this.FullURL += "&";
        }
    }
    if (null == val)
        val = '';
        
    val = escape(val);
    
    if (null != replaceIfExists && true == replaceIfExists)
    {
        /* if the paramater already exists, find it in the string and replace it
        */
        var tokenBefore= "?";
        var toFind     = tokenBefore + name + "=";
        var toFindLen  = toFind.length; 
        var itemIndex  = this.FullURL.indexOf("?"+name+"=");
        if (-1 == itemIndex)
        {
            tokenBefore = "&";
            toFind      = tokenBefore + name + "=";
            itemIndex   = this.FullURL.indexOf(toFind);
        }
        
        if (itemIndex > 0)
        {
            var finalString = this.FullURL.substring(0, itemIndex+1);
            var rest = this.FullURL.substring(itemIndex+toFindLen);
            var nextToken = rest.indexOf("&");
            
            if (-1 != rest)
                rest = rest.substring(nextToken);
            
            this.FullURL = finalString + name + "=" + val + rest;
        }
        else
            this.FullURL += name + "=" + val;
    }
    else
        this.FullURL += name + "=" + val;
}

AjaxURL.prototype.AddParams = function(hashParams, replaceIfExists)
{
	if (null == hashParams)
		return;
		
	var ajaxUrl = this;
	hashParams.each(function(pair) 
	{
  		ajaxUrl.AddParam(pair.key, pair.value, replaceIfExists);
	});
}

AjaxURL.prototype.AddFormParam = function(formItem, replaceIfExists)
{
	this.AddParam(formItem, getElemValue(formItem), replaceIfExists)
}

/*
 * AddRandom() - Adds a random number to the paramaters
 */
AjaxURL.prototype.AddRandom = function()
{
	var t = "" + Math.random();
	
	if (t.length > 0)
		this.AddParam("random", t.substring(2), true);
}

/*
 * GetURL() - Retrieves the complete URL
 */
AjaxURL.prototype.GetURL = function()
{
	return this.FullURL;
}

/*
 * ToString() - Retrieves the complete URL
 */
AjaxURL.prototype.ToString = function()
{
	return this.FullURL;
}

/*
 * Simple call for Ajax
 */
CallAjax = function(routine, params, returnFirstResult)
{
	var ajax = new AjaxObject(AJAX_SERVER_URL);
	var results = null;

	ajax.WebURL.AddParams(params, true);
	ajax.WebURL.AddParam("req", routine);
	ajax.Open();

	if(ajax.HasResults)
	{
		results = (true == returnFirstResult) ? ajax.ResultSet.GetFirstResult() : ajax.ResultSet;
	}
	
	ajax.Close();
	
	return results;
}
