/*
 SuggestionMaker is a class for making drop-down lists under HTML text inputs.
It gets it list by making an HTTP request with the AJAX class and using some CSS
stylesheet classes.

 Each text input must have a <div/> with an id. The <div/> will be positioned by
this class under the <input/> element and filled with a list for the user to ch-
oose from. The ids of the div and text input must be given to an object of the
SuggestionMaker class, before it can be used:

 <script>
 // Response handler.
 function responseHandler(ajax,req)
 {if(ajax.readyState==4&&(ajax.getStatus()==200||ajax.getStatus()==304)){
	// Response received...
 }}
 var suggestion1=new SuggestionMaker(
           'suggestDiv1',// Div id.
           'textInput1', // Text input id.
           'states.php',// Request URL, without query string.
           true,	// Boolean flag. If true, list becomes visible when
			// the HTTP response is received. Optional argument.
           responseHandler);	// Callback function. Same as AJAX callbacks,
				// and it should take the same arguments. This
				// argument is optional.
     suggestion2=new SuggestionMaker('suggestDiv2','textInput2','states.php');
 </script>
 <!-- A form for choosing states... -->
 <form
    action='submit.php'
    method='GET'
    name='form1'>
    <!-- First input. -->
    <input
       id='textInput1'
       name='state1'
       onblur='suggestionMaker.hide();'
       onclick='suggestionMaker.toggle();'
       ondblclick='suggestionMaker.sendRequest("state="+escape(this.value),"GET");'
       
       type='text'
       value='State Name'/><br/>
    <!-- Second input. Same as first, but uses suggestion2! -->
    <input
       id='textInput2'
       name='state2'
       onblur='suggestionMaker2.hide();'
       onclick='suggestionMaker2.toggle();'
       ondblclick='suggestionMaker2.sendRequest("state="+escape(this.value),"GET");'
       type='text'
       value='Another State Name'/><br/>
       <!-- Make div elements. -->
       <div id='suggestDiv1'/><div id='suggestDiv2'/>
       <!-- ... -->
 </form>

 In this example, there are 2 text inputs. When an input loses focus, the drop-
down list is hidden. When the input is clicked, the list is toggled, which means
the if it were hidden, it would be shown, and if it were showing, it will be hi-
dden. When an input is double-clicked, the SuggestionMaker sends an HTTP request
to 'states.php' with the GET query string "state=TEXT" where TEXT is the value
of the input element at the time. When the response comes back, the XML is pars-
ed, the list of new suggestions is made, and the list is shown!

 The XML response must have the following structure:

<suggestions>
<suggestion>Suggestion 1</suggestion>
<suggestion>Suggestion 2</suggestion>
<suggestion>Suggestion 3</suggestion>
...
</suggestions>

 So, the above example, the XML from states.php might look like this:

<suggestions>
<suggestion>New Jersey</suggestion>
<suggestion>New Mexico</suggestion>
<suggestion>New York</suggestion>
</suggestions>

 Joshua A. S. Allen.
 */

function SuggestionMaker(divId,inputId,requestURL,popupOnRes,resHandler)
{// You should never have to set any of these vars!
this.httpRequest=new AJAX(requestURL);
this.httpRequest.requesting=false;
this.httpRequest.suggestionMaker=this;
this.visibleNow=false;
this.inputId=inputId;
this.divId=divId;
// In case we have a SuggestionManager...
this.suggestionManager=null;
// MouseDown event handler function name.
// Must expect three arguments: inputId, drop-down list item text, list item index.
this.itemHandler='';
// Array of strings for list.
this.listItems=new Array();
// Popup on response flag (default is true).
this.popupOnResponse=arguments.length>=4?popupOnRes:true;
// Response handler callback.
this.responseHandler=arguments.length>=5?resHandler:null;

// Set these here, or in your pages to change
// the CSS class names.
this.suggestionClass='suggestion';
this.invisibleClass='invisibleSuggestions';
this.visibleClass='visibleSuggestions';

this.isVisible=function()
{// Returns true if list is visible, false if not.
return(this.visibleNow);}

this.getDivOffset=function(f)
{// Calculate the left or top offset of the div.
var i=document.getElementById(this.inputId);
var o=f=='offsetTop'?i.offsetHeight:0;
for(;i;i=i.offsetParent){
	o+=i[f];}
return(o);}

this.show=function()
{// Show drop-down list.
var d=document.getElementById(this.divId);
var k=0;
var s='';
// Found div?
if(d){
	// Empty it.
	d.innerHTML='';
	// Concatenate inner divs...
	for(k=0;k<this.listItems.length;k++){
		s+='<div '+
		'class="'+this.suggestionClass+'" '+
		'onmousedown="this.parentNode.className=\''+this.invisibleClass+'\';document.getElementById(\''+this.inputId+'\').value=\''+this.listItems[k]+'\';'+(this.itemHandler?this.itemHandler+'(\''+this.inputId+'\',\''+this.listItems[k]+'\','+k+');':'')+'">'+
		this.listItems[k]+'</div>';}
	// Change innerHTML of parent div.
	d.innerHTML=s;
	// Set class and style.
	d.className=this.visibleClass;
	d.style.visibility='visible';
	d.style.top=this.getDivOffset('offsetTop')+'PX';
	d.style.left=this.getDivOffset('offsetLeft')+'PX';
	// We should now be visible!
	return(this.visibleNow=true);}
// Div element not found!
return(this.visibleNow=false);}

this.hide=function()
{// Hides drop-down list.
var d=document.getElementById(this.divId);
// Hide div if found.
if(d){
	d.className=this.invisibleClass;
	d.style.visibility='hidden';
	return(this.visibleNow=false);}
// Unable to hide div!
return(this.visibleNow=true);}

// Show list if hidden, hide if visible.
this.toggle=function()
{return(this.visibleNow?this.hide():this.show());}

this.sendRequest=function(qs,meth,p,rh)
{// Send request with query string qs using HTTP
// method meth. If meth not passed, defaults to
// using 'GET'. String qs, popup flag, and response
// handler arguments optional.
qs=arguments.length==0?'':qs;
meth=arguments.length==1?'GET':arguments[1];
this.popupOnResponse=arguments.length>=3?p:this.popupOnResponse;
this.responseHandler=arguments.length>=4?rh:this.responseHandler;
this.httpRequest.method=meth;
if(meth.toUpperCase()=='GET'){
	this.httpRequest.clearRequestHeaders();
	this.httpRequest.get=qs;}
if(meth.toUpperCase()=='POST'){
	this.httpRequest.clearRequestHeaders();
	// This MUST be set for POST requests!
	this.httpRequest.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
	this.httpRequest.post=qs;}
// If in the middle of another request, abort it.
if(this.httpRequest.requesting){
	this.httpRequest.abort();}
// Make onreadystatechange function.
this.httpRequest.onreadystatechange=function(aj,httpReq)
	{aj.requesting=!(aj.readyState==4);
	if(aj.readyState==4&&(aj.getStatus()==200||aj.getStatus()==304)){
		aj.suggestionMaker.makeList();
		aj.suggestionMaker.popupOnResponse?aj.suggestionMaker.show():false;}
	if(typeof aj.suggestionMaker.responseHandler=='function'){
		aj.suggestionMaker.responseHandler(aj,httpReq);}}
// If AJAX is supported in this browser, sendRequest().
if(this.httpRequest.AJAXSupported()){
	this.httpRequest.sendRequest();
	return(this.httpRequest.requesting=true);}
else{
	return(this.httpRequest.requesting=false);}}

this.makeList=function()
{// Return array of suggestions from XML.
var suggestions=this.httpRequest.getXML().documentElement.childNodes;
var k=0;
this.listItems.length=0;
// Go over each child of the root element.
for(;k<suggestions.length;k++){
	// Make sure XML element is a suggestion.
	if(suggestions[k].nodeName!=='suggestion'){
		continue;}
	// Attempt to get text from it!
	this.listItems[this.listItems.length]=(typeof suggestions[k].text=='string'?suggestions[k].text:typeof suggestions[k].textContent=='string'?suggestions[k].textContent:typeof suggestions[k].nodeValue=='string'?suggestions[k].nodeValue:typeof suggestions[k].innerText=='string'?suggestions[k].innerText:null);
	// Could we get text?
	if(this.listItems[0]==null){
		// No text.
		this.listItems.length=0;
		break;}}}}
