//-------------- BEGIN CallBackObject --------------------
function CallBackObject()
{
  this.XmlHttp = this.GetHttpObject();
}
 
CallBackObject.prototype.GetHttpObject = function()
{ 
	var xmlhttp=false;

	try {
		xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
	} catch (e) {
		try {
			xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		} catch (E) {
			xmlhttp = false;
		}
	}
	
	if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
		try {
			xmlhttp = new XMLHttpRequest();
		} catch (e) {
			xmlhttp=false;
		}
	}
	
	if (!xmlhttp && window.createRequest) {
		try {
			xmlhttp = window.createRequest();
		} catch (e) {
			xmlhttp=false;
		}
	}
	
	return xmlhttp;
}
 
CallBackObject.prototype.DoCallBack = function(eventTarget, eventArgument)
{
   
  var theData = '';
  var theform = document.forms[0];
  var thePage = window.location.href; //window.location.pathname + window.location.search;

  var eName = '';
   
   
  
  theData  = '__EVENTTARGET='  + escape(eventTarget.split("$").join(":")) + '&';
  theData += '__EVENTARGUMENT=' + eventArgument + '&';
  theData += '__VIEWSTATE='    + escape(theform.__VIEWSTATE.value).replace(new RegExp('\\+', 'g'), '%2b') + '&';
  theData += 'IsCallBack=true&';
  
  for( var i=0; i<theform.elements.length; i++ )
  {
    eName = theform.elements[i].name;
    if( eName && eName != '')
    {
      if( eName == '__EVENTTARGET' || eName == '__EVENTARGUMENT' || eName == '__VIEWSTATE' )
      {
        // Do Nothing
      }
      else
      {
        theData = theData + escape(eName.split("$").join(":")) + '=' + theform.elements[i].value;
        if( i != theform.elements.length - 1 )
          theData = theData + '&';
      }
    }
  }

  if( this.XmlHttp )
  {
  
    if( this.XmlHttp.readyState == 4 || this.XmlHttp.readyState == 0 )
    {
      var oThis = this;
      this.XmlHttp.open('POST', thePage, true);
      this.XmlHttp.onreadystatechange = function(){ oThis.ReadyStateChange(); };
      this.XmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

      this.XmlHttp.send(theData);
    }
  }
}
 
CallBackObject.prototype.AbortCallBack = function()
{
  if( this.XmlHttp )
    this.XmlHttp.abort();
}
 
CallBackObject.prototype.OnLoading = function()
{
  // Loading
}
 
CallBackObject.prototype.OnLoaded = function()
{
  // Loaded
}
 
CallBackObject.prototype.OnInteractive = function()
{
  // Interactive
}
 
CallBackObject.prototype.OnComplete = function(responseText, responseXml)
{
  // Complete
}
 
CallBackObject.prototype.OnAbort = function()
{
  // Abort
}
 
CallBackObject.prototype.OnError = function(status, statusText)
{
  // Error
}
 
CallBackObject.prototype.ReadyStateChange = function()
{
  if( this.XmlHttp.readyState == 1 )
  {
    this.OnLoading();
  }
  else if( this.XmlHttp.readyState == 2 )
  {
    this.OnLoaded();
  }
  else if( this.XmlHttp.readyState == 3 )
  {
    this.OnInteractive();
  }
  else if( this.XmlHttp.readyState == 4 )
  {

    if( this.XmlHttp.status == 0 )
    {
      this.OnAbort();
    } else if( this.XmlHttp.status == 200 && this.XmlHttp.statusText == "OK" ) {
      this.OnComplete(this.XmlHttp.responseText, this.XmlHttp.responseXML);
      
    } else {
      this.OnError(this.XmlHttp.status, this.XmlHttp.statusText, this.XmlHttp.responseText);   
    }
  }
}
//----------------End CallBackObject---------------------

//----------------BEGIN AutoCompleteTree ---------------------------
function AutoCompleteTree() {
	//set the initial values
	this.bEnd = false;
	this.nCount = 0;
	this.aStr = new Object;
}

AutoCompleteTree.prototype.add = function(str) {
	//increment the count value
	this.nCount++;
	
	//if at the end of the string, flag this node as an end point
	if (str == "")
		this.bEnd = true;
	else
	{
		//otherwise, pull the first letter off the string
		var letter = str.substring(0,1);
		var rest = str.substring(1,str.length);
		
		//and either create a child node for it or reuse the old one.
		if (!this.aStr[letter]) this.aStr[letter] = new AutoCompleteTree();
		this.aStr[letter].add(rest);
	}
}

AutoCompleteTree.prototype.getCount = function(str, bExact) {
	//if end of search string, return number
	if ( str == "") 
		if (this.bEnd && bExact && (this.nCount ==1)) return 0;
		else return this.nCount;
		
	//otherwise, pull the first letter off the string
	var letter = str.substring(0,1);
	var rest = str.substring(1,str.length);
	
	//and look for case-insensitive matchaes
	var nCount = 0;
	var lLetter = letter.toLowerCase();
	if ( this.aStr[lLetter])
		nCount += this.aStr[lLetter].getCount(rest, bExact && (letter == lLetter));
	var uLetter = letter.toUpperCase();
	if (this.aStr[uLetter])
		nCount += this.aStr[uLetter].getCount(rest, bExact && (letter == uLetter));
	return nCount;
}


AutoCompleteTree.prototype.getStrings = function(str1, str2, outStr) {

	if ( str1 == "")
	{
		//add matching string
		if ( this.bEnd )
			outStr.push(str2);
		
		//get strings for each child node
		for ( var i in this.aStr )
			this.aStr[i].getStrings(str1, str2 + i, outStr);
	} 
	else
	{
		//pull the first letter off the string
		var letter = str1.substring(0,1);
		var rest = str1.substring(1, str1.length);
		
		//and get the case-insensitive matches
		var lLetter = letter.toLowerCase();
		if ( this.aStr[lLetter])
			this.aStr[lLetter].getStrings(rest, str2 + lLetter, outStr);
		
		var uLetter = letter.toUpperCase();
		if ( this.aStr[uLetter])
			this.aStr[uLetter].getStrings(rest, str2 + uLetter, outStr);
	}

}
//END AutoCompleteTree
//----------------BEGIN AutoCompleteTextBox ------------------------

function AutoCompleteTextBox(TextBoxId, DivId, DivClass)
{
/*
	// initialize member variables
	var oThis = this;
	var oText = document.getElementById(TextBoxId);
	var oDiv  = document.getElementById(DivId);
	
	//For speed, we'll need the AutoCompleteTextBox to pull data from 
	//a local tree structure (really just a composite pattern) 
	//rather than from the server
	//this db is populated after a user enters 1 letter 
	
	this.db = new AutoCompleteTree();
	
	this.TextBox = oText;
	this.Div = oDiv;
	this.nMaxSize = 8; // maximum number of tags to show in dropdown;
	
	// CallBackObject + Event Handlers
	this.Cbo = new CallBackObject();
	this.Cbo.OnComplete = function(responseText,responseXML){oThis.Cbo_Complete(responseText,responseXML);};
	this.Cbo.OnError    = function(status,statusText,responseText){oThis.Cbo_Error(status,statusText,responseText);};
			
	// attach handlers to the TextBox
	oText.AutoCompleteTextBox = this;
	oText.onkeyup = AutoCompleteTextBox.prototype.OnKeyUp;
	oText.onblur  = AutoCompleteTextBox.prototype.OnBlur;
	
	// align the drop down div
	var c = GetCoords(oText);
	var n = oText.style.pixelHeight;
	if( !n )
	{ 
	  n = 25;
	}
	else
	{
	  n += 2;
	}
	//hardcode here
	oDiv.style.left = 0;
	oDiv.style.top = 18;
	oDiv.style.display = 'none';
	oDiv.style.position = 'absolute';
	
	// Set some default styles
	if( DivClass )
	  oDiv.className = DivClass;
	else
	{
	  oDiv.style.border = '1';
	  oDiv.style.borderColor = 'black';
	  oDiv.style.borderStyle = 'solid';
	  oDiv.style.backgroundColor = 'white';
	  oDiv.style.padding = '2';
	}
	*/
}

AutoCompleteTextBox.prototype.DoAutoSuggest = true;

AutoCompleteTextBox.prototype.ListItemClass = '';

AutoCompleteTextBox.prototype.ListItemHoverClass = '';

// TextBox OnBlur
AutoCompleteTextBox.prototype.OnBlur = function()
{
	this.AutoCompleteTextBox.TextBox_Blur();
}

AutoCompleteTextBox.prototype.TextBox_Blur = function()
{
	this.Div.style.display='none';
}

// TextBox OnKeyUp
AutoCompleteTextBox.prototype.OnKeyUp = function(oEvent)
{
  //check for the proper location of the event object

  if (!oEvent) 
  {
    oEvent = window.event;
  }

  //this.AutoCompleteTextBox.TextBox_KeyUp(oEvent);      
}

AutoCompleteTextBox.prototype.ShowDivPopup = function()
{
  var txt = this.TextBox.value;
  //count the number of strings that match the text-box value
  var nCount = this.db.getCount(txt, true);
 
  //if a suitable number is found, then show the popup-div
 if ((this.nMaxSize == -1) || ((nCount < this.nMaxSize) && (nCount > 0))) {
		//clear the div
		while ( this.Div.hasChildNodes() )
			this.Div.removeChild(this.Div.firstChild);
			
		//get all the matching strings from the AutoCompleteTree
		var aStr = new Array();
	
		this.db.getStrings(txt, "", aStr);
		
		//add each string to the popup-div
		var i, n = aStr.length;
		for (i = 0; i < n; i++) {
			var oDiv = document.createElement('div');
			this.Div.appendChild(oDiv);
			oDiv.innerHTML = aStr[i];
			oDiv.noWrap       = true;
			oDiv.style.width  = '100%';
			oDiv.className    = this.ListItemClass;
			oDiv.onmousedown  = AutoCompleteTextBox.prototype.Div_MouseDown;
			oDiv.onmouseover  = AutoCompleteTextBox.prototype.Div_MouseOver;
			oDiv.onmouseout   = AutoCompleteTextBox.prototype.Div_MouseOut;
			oDiv.AutoCompleteTextBox = this;
		} 

		this.Div.style.display = 'block';
		this.Div.style.visibility = 'visible';
		if( this.DoAutoSuggest == true )
			this.AutoSuggest( aStr );
  } else {
		
		this.Div.innerHTML = "";
		this.Div.style.visibility = "hidden";
  }
}

AutoCompleteTextBox.prototype.TextBox_KeyUp = function(oEvent)
{
  var iKeyCode = oEvent.keyCode;

  if( iKeyCode == 8 )
  {
    this.Div.innerHTML = '';
		this.Div.style.display = 'none';
		return;
  }
  else if( iKeyCode == 16 || iKeyCode == 20 )
  {
    this.DoAutoSuggest = true;
  }
  else if (iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode <= 46) || (iKeyCode >= 112 && iKeyCode <= 123)) 
  {
    return;
  }
  else
  {
    this.DoAutoSuggest = true;
  }

	var txt = this.TextBox.value;
	
	if( txt.length > 0 )
	{
		//If we need to update the information
		//we want to store as much as possible client side,
		//but only as much as makes sense
		//so we are going to get all the tags for the first letter
		//if you want to narrow it for performance, change this line	  
	  if (! this.db.aStr[txt.substring(0,2)]) {
		this.Cbo.DoCallBack(this.TextBox.name, txt);
		this.ShowDivPopup();
	  } else {
		this.ShowDivPopup();
	  }

	}
	else
	{
	    this.Div.innerHTML = '';
		this.Div.style.display = 'none';
		this.Cbo.AbortCallBack();
	}
}

AutoCompleteTextBox.prototype.Cbo_Complete = function(responseText, responseXML)
{ 
  
  this.db = new AutoCompleteTree();

			
	// get all the matching strings from the server response
	var aStr = responseText.split('\n');
			
	// add each string to the popup-div
	var i, n = aStr.length;
	
	//populate AutoCompleteTree
	if( n > 0 )
	{
	  for ( i = 0; i < n; i++ )
	  {
			this.db.add(aStr[i]);		
	  }
	}
	
}

AutoCompleteTextBox.prototype.Cbo_Error = function(status, statusText, responseText)
{
  //alert('CallBackObject Error: status=' + status + '\nstatusText=' + statusText + '\n' + responseText);
}

AutoCompleteTextBox.prototype.Div_MouseDown = function()
{
	this.AutoCompleteTextBox.TextBox.value = this.innerHTML;
}

AutoCompleteTextBox.prototype.Div_MouseOver = function()
{
  if( this.AutoCompleteTextBox.ListItemHoverClass.length > 0 )
	  this.className = this.AutoCompleteTextBox.ListItemHoverClass;
	else
	{
	  this.style.backgroundColor = 'black';
    this.style.color = 'white';
	}
}

AutoCompleteTextBox.prototype.Div_MouseOut = function()
{
  if( this.AutoCompleteTextBox.ListItemClass.length > 0 )
	  this.className = this.AutoCompleteTextBox.ListItemClass;
	else
	{
		this.style.backgroundColor = 'white';
		this.style.color = 'black';
	}
}

AutoCompleteTextBox.prototype.AutoSuggest = function(aSuggestions /*:array*/) 
{
  if (aSuggestions.length > 0) 
  {
    this.TypeAhead(aSuggestions[0]);
  }
}

AutoCompleteTextBox.prototype.TypeAhead = function( sSuggestion /*:string*/)
{
  if( this.TextBox.createTextRange || this.TextBox.setSelectionRange)
  {
    var iLen = this.TextBox.value.length; 
    this.TextBox.value = sSuggestion; 
    this.SelectRange(iLen, sSuggestion.length);
  }
}

AutoCompleteTextBox.prototype.SelectRange = function (iStart /*:int*/, iLength /*:int*/) 
{
  //use text ranges for Internet Explorer
  if (this.TextBox.createTextRange) 
  {
    var oRange = this.TextBox.createTextRange(); 
    oRange.moveStart("character", iStart); 
    oRange.moveEnd("character", iLength - this.TextBox.value.length);      
    oRange.select();
   
  //use setSelectionRange() for Mozilla
  } 
  else if (this.TextBox.setSelectionRange) 
  {
      this.TextBox.setSelectionRange(iStart, iLength);
  }     

  //set focus back to the textbox
  this.TextBox.focus();      
}

//----------------END AutoCompleteTextBox ------------------------
             
function GetCoords(obj /*:object*/) 
{   
  var newObj = new Object();
  
  newObj.x = obj.offsetLeft - 30;
  newObj.y = obj.offsetTop - 20;
  theParent = obj.offsetParent;
  while(theParent != null)
  {
    newObj.y += theParent.offsetTop;
    newObj.x += theParent.offsetLeft;
    theParent = theParent.offsetParent;
  }
  
  return newObj;
}
