//
//	addresslookup.js			requires request.js utility.js
//
//	Lookup address components on the server - return the results in XML
//
var _addressRequestor = null;								// communications object
var _currentInputFieldValue;								// current value that we will look up
var _oldInputFieldValue;								// previous value that we have looked up
// var _previousLookups = new Array();							// cach of previous lookups
var _serviceid;
var _ratetypeid;
var _inputField;									// the control whos data we are looking up
var _nextControl;									// next control in sequence
var _mouseClicked;									// was the selection made with a mouse click
var _searchType;									// The type of search - address, street or suburb
var _regionID;										// regionid of the pickup address
var _searchQualifier;									// qualifier - pickup, forward, region, etc
var _completeDiv = null;								// The DIV containing the address list
var _lastKeyCode
var _timer;
var _highlightedSuggestionIndex=-1; 							// currently hightlighted suggestion index (was j)
var _highlightedSuggestionDiv=null; 							// currently highlisted suggestion div... (was h)
var _completeDivRows=-1; 								// completeDiv rows at time of keypress... (was Z)
var _completeDivDivList=null; 								// completeDiv div list at time of keypress (was za)
var _divTag = "div";
var _blurHandler;									// the previous onblur handler for the input control
var _region;										// the region to select addresses from
var _used;									// did we ever form any list?

function clearControl(fld) {
   fld.item = null; 
	 _used = false;	
}

function useAddressLookup(fld, searchtype, qualifier, nextcontrol)
//
//	Use the address lookup function for this control on the form.
//	set this function in the onfocus event of the field that is to contain the address
//
//	fld		The Field that we are looking up data for
//	searchtype	The type of search - address, street or locality
//	qualifier	Search type quelifier - pickup, forward, regionid or localityid
//	nextcontrol	The control to receive focus when address selection is complete
{
	_inputField = fld;
	if (!searchtype) {
		_searchType = "address";
	} else {
		_searchType = searchtype;
	}
	_nextControl = nextcontrol;
	_searchQualifier = qualifier;
	_regionID =  "";				    // do not need to know about regionid			
  _serviceid = "";				// do not need to know about serviceid				
  _ratetypeid = "";			 			// do not need to know about ratetypeid
	setupControl()
}
function serverRequest(request, qualifier, procFunctionName, regionid, serviceid, ratetypeid)
//
//	Make a request of the server - This will utilize the serveAddresses server to handle other requests
//	Call this function when a request needs to be made
//
//	request		The type of request - so far abn for ABN lookup, email for email address validation or delay to delay response
//	qualifier	request qualifier
//	nextcontrol	The control to receive focus when address selection is complete
{
	if (qualifier) {								// ensure that we have something to request
		_inputField = null;
		_searchType = request;
		_searchQualifier = qualifier;
	  _serviceid = serviceid;
	  _ratetypeid = ratetypeid;			 
		_nextControl = null;
		_blurHandler = procFunctionName;
		_regionID = regionid;
		getRequest(null, null, handleRequest);					// make the request
	}
}
function handleRequest()
//
//	Handle the response to a request
{
	// Make sure that the transaction has finished. The XMLHttpRequest object 
	//	has a property called readyState with several states:
	//	0: Uninitialized
	//	1: Loading
	//	2: Loaded
	//	3: Interactive
	//	4: Finished 
	if (_addressRequestor.readyState == 4) { 					// Finished loading the response?
		if (_addressRequestor.responseXML && _blurHandler) {			// Did we receive a response?
				if (_addressRequestor.status >=00 && _addressRequestor.status < 300) {
				var response = _addressRequestor.responseXML;		// extract the XML into a DOM object
				var text = _addressRequestor.responseText;
				_blurHandler(false, response, text);			// call the function to process it
			} else {
				_blurHandler(_addressRequestor.status+" "+_addressRequestor.statusText);
			}
		}
	}
}
function XMLvalue(control, name)
//
//	Get the value of the named XML field from the XML object stored in the control
{
	return getElementText(name, control.item, 0);
}
function XMLtext(xmlObject, name)
//
//	Get the named XML field from the XML object
{
	return getElementText(name, xmlObject, 0);
}

function displayAddress(ctrl, suburb, state, postcode, country)
//
//	Display the address components from the previously loaded XML
{
	if (ctrl.item) {
		ctrl.value = XMLfield(ctrl.item, "address.name");
		suburb.value = XMLfield(ctrl.item, "address.suburb");
		if (state)
		   state.value = XMLfield(ctrl.item, "address.state");
		if (postcode)
    	  postcode.value = XMLfield(ctrl.item, "address.postcode");
		if (country)
		   country.value = "Australia";
	}
}

function displaySelectedSuburb(ctrl, state, postcode, country)
//
//	Display the full selected suburb in the control
{

	if (ctrl.item) {
    _used = true;
		if (!state) {
			ctrl.value = XMLfield(ctrl.item, "locality.suburb")+", "+XMLfield(ctrl.item, "locality.state")+", "+XMLfield(ctrl.item, "locality.postcode");
		} else {
			ctrl.value = XMLfield(ctrl.item, "locality.suburb");
			if (state)
			    state.value = XMLfield(ctrl.item, "locality.state");
			if (postcode)
				postcode.value = XMLfield(ctrl.item, "locality.postcode");
			if (country)
				country.value = "Australia";
		}
	} else {
 		 if (_used && _currentInputFieldValue != "") {
		    displayError("The locality entered does not exist on our database.<br>Please re-check and re-enter the correct "+_searchType+".", _inputField);
	      ctrl.focus();
			}   
	}
}

function getAddress() 
//
//	Lookup this address on the server
{

	_timer = 0;									// timer is no longer running
	_currentInputFieldValue=_inputField.value;
	if (_currentInputFieldValue == _oldInputFieldValue)				// has the field value changed?
		return;									// no - forget it
	_oldInputFieldValue = _currentInputFieldValue;					// save this value
	var disqualified;								// are we qualified to lookup?
	if (_searchType == 'address') {							// if we are looking up an address
		var spaceChar = _currentInputFieldValue.indexOf(" ");			// find a space
		if (spaceChar > -1)							// if there is one
			disqualified = spaceChar == (_currentInputFieldValue.length-1);	// we qualify to lookup if the first space isn't the end 
		else
			disqualified = true;						// wait for a space
	} else {									// for any other lookup
		disqualified = false;							// we qualify without a space
	}
	_regionID =  "";				    // do not need to know about regionid			
  _serviceid = "";				// do not need to know about serviceid				
  _ratetypeid = "";			 			// do not need to know about ratetypeid

	if (disqualified) {								// if we are not ready to lookup yet
		hideCompleteDiv();							// no - nothing will be found and there is no list
		return;									// thats it for me
	}
	getRequest(10, _currentInputFieldValue, handleAddress);				// make the request
}
function getRequest(rows, inputValue, procFunction)
//
//	Send the request to the server to be processed
{
  if (_serviceid == undefined || _serviceid == null) 
	    _serviceid = ""
	if (_ratetypeid == undefined  || _ratetypeid == null)
		  _ratetypeid = ""
			 
//	var message = '<?xml version="1.0" encoding="ISO-8859-1"?><lookup><regionid>'+_regionID+'</regionid><type>'+_searchType+'</type><qualifier>'+_searchQualifier+'</qualifier><servicetypeid>'+_servicetypeid+'</servicetypeid><ratetypeid>'+_ratetypeid+'</ratetypeid>';
	var message = '<?xml version="1.0" encoding="ISO-8859-1"?><lookup><regionid>'+_regionID+'</regionid><type>'+_searchType+'</type><qualifier>'+_searchQualifier+'</qualifier><serviceid>'+_serviceid+'</serviceid><ratetypeid>'+_ratetypeid+'</ratetypeid>';
	if (rows)
		message += '<rows>'+rows+'</rows>';
	if (inputValue)
		message += '<input>' + inputValue + '</input>';
	message += '</lookup>';

	if (!_addressRequestor) {							// If we don't have a communications object
		_addressRequestor = createRequestor();					// create one
		if (!_addressRequestor)							// if we can't create one
			return;								// just don't work
	}
	if (_addressRequestor.readyState != 4 && _addressRequestor.readyState != 0) {	// are we processing a request?
		_addressRequestor.onreadystatechange = function(){};			// ignore readystate change from abort()
		_addressRequestor.abort();						// teminate outstanding request
		_addressRequestor = createRequestor();					// and get a brand new requestor
	}
	var requestURL = location.protocol + "//" + location.hostname + "/ajax/serveAddress/";
	_addressRequestor.open('POST', requestURL, true);				// set up the request
	_addressRequestor.onreadystatechange = procFunction;				// set up the function to handle the response
	_addressRequestor.setRequestHeader('Content-Type', 'text/xml; charset=UTF-8');	// Mark it as an XML request
	_addressRequestor.send(message);
}
function handleAddress()
//
//	The communications state has changed.  If it has changed to finished then read and process the address list
{
	// Make sure that the transaction has finished. The XMLHttpRequest object 
	//	has a property called readyState with several states:
	//	0: Uninitialized
	//	1: Loading
	//	2: Loaded
	//	3: Interactive
	//	4: Finished 
	if (_addressRequestor.readyState == 4){ 					// Finished loading the response?
		if (_addressRequestor.status == 200) {					// OK response
			if (_addressRequestor.responseXML) {				// have we received a response?
				var response = _addressRequestor.responseXML;		// extract the XML into a DOM object
				var inputText = XMLtext(response, "input");		// get the input string for this query
	//			cacheResults(inputText, response);			// and stick it in cache
				displaySuggestedList(_completeDiv, response);		// display the returned list of addresses
			}
		} else {
			alert(_addressRequestor.status+" "+_addressRequestor.statusText);
		}
	}
}
function showCompleteDiv()
//
//	Ensure that the address list is visible
{
	if (_completeDiv) {
		_completeDiv.style.visibility = "visible";				// make it visible
		setCompleteDivSize();							// ensure it is the correct size and in the right place
	}
}

function setupControl()
//
//	Setup the control for use by the address lookup functions
{
	_inputField.autocomplete="off";							// stop the browser from guessing input
	_inputField.select();								// select the full text
	_blurHandler = _inputField.onblur;						// save the onblur handler
	_inputField.onblur=onBlurHandler;						// and replace it with ours
	_mouseClicked = false;
	if(_inputField.createTextRange) {
		_inputField.onkeyup=new Function("return keyupHandler(event);");
	} else {
		_inputField.onkeyup=keyupHandler;
	}
	_currentInputFieldValue=_inputField.value;					// save the current field contents
	_oldInputFieldValue=_currentInputFieldValue;
	if (!_completeDiv) {								// Have we been here before?
		_completeDiv=document.createElement("DIV");				// no - create the DIV for the address list
		_completeDiv.id="completeDiv";
		_completeDiv.style.border="black 1px solid";
		_completeDiv.style.zIndex="1";
		_completeDiv.style.padding="0";
		setCompleteDivSize();
		_completeDiv.style.visibility="hidden";
		_completeDiv.style.position="absolute";
		_completeDiv.style.backgroundColor="white";
		document.body.appendChild(_completeDiv);				// add it to the document
		setStyleForElement(_completeDiv,"m-divContainer")
	}
	setCompleteDivSize();
	_completeDiv.style.visibility="hidden";
	window.onresize=resizeHandler;
	_highlightedSuggestionIndex=-1; 						// currently hightlighted suggestion index (was j)
	_highlightedSuggestionDiv=null; 						// currently highlisted suggestion div... (was h)
	_completeDivRows=-1; 								// completeDiv rows at time of keypress... (was Z)
	_completeDivDivList=null; 							// completeDiv div list at time of keypress (was za)
}

function resizeHandler()
//
//	If the input field or document has been resized adjust the position and size of the address DIV
{
	setCompleteDivSize()
}

function setCompleteDivSize()
//
//	Adjust the position and size of the address DIV to match the input field
{
	if (_completeDiv && _inputField) {
		_completeDiv.style.left=calculateOffsetLeft(_inputField)+"px";
		_completeDiv.style.top=calculateOffsetTop(_inputField)+_inputField.offsetHeight-1+"px";
//		_completeDiv.style.width=calculateWidth(0)+"px"
	}
}

function calculateWidth(override)
//
//	Calculate the width of the input field ensuring that it is no narrower than the specified content width
{
	var fldWidth = override;
	if (fldWidth == 0)
		fldWidth = _inputField.offsetWidth
	if(navigator&&navigator.userAgent.toLowerCase().indexOf("msie")==-1){
		fldWidth = fldWidth-2
	}
	return fldWidth;
}
keyupHandler=function(e)
//
//	Handle the key up event - process the resulting text
{
	_lastKeyCode=e.keyCode;
	if (_lastKeyCode==40) {								// down arrow
		highlightNewValue(_highlightedSuggestionIndex+1);
		return;
	}else if (_lastKeyCode==38) {							// up arrow
		highlightNewValue(_highlightedSuggestionIndex-1);
		return;
	}else if (_lastKeyCode==13||_lastKeyCode==3||_lastKeyCode==9||_lastKeyCode==10) {// enter, dunno, tab, new line?
		if (_completeDivDivList) {						// have we entered any address?
			if (_nextControl) {						// has the caller specified a control to receive focus?
				_nextControl.focus();					// pass the focus over
			} else {							// we dont know where we are going
				_inputField.blur();					// just lose focus ourself
			}
		}
		return;
	}
	if (_timer) {									// are we waiting for the next keystroke?
		clearTimeout(_timer);							// yes - stop waiting
		_timer = 0;
	}
	if (_inputField.value) {							// only process if there is data
		_currentInputFieldValue=_inputField.value;				// save the current input text
		if (_currentInputFieldValue != _oldInputFieldValue)			// has the control value changed?
			_inputField.item = null;					// clear any previous selection
//		var response = _previousLookups[_currentInputFieldValue];		// and look it up in the cache
//		if (response) {								// is it there?
//			displaySuggestedList(_completeDiv, response);			// display whatever matches it
//		} else {								// we havent looked for this before
			_timer = setTimeout("getAddress()", 150);			// lookup the address in 150 milliseconds
//		}
	}
}
function onBlurHandler(event)
//
//	The user has left the address field.  If a line has been selected then save the details
//	turn off the address list
{
	if(!event&&window.event) {
		event=window.event;
	}
	if (!_inputField.item) {							// has a selection been made? (mouse click)
		if (!_highlightedSuggestionDiv) {					// no - is an address highlighted?
			highlightNewValue(0);						// no - highlight the first address (if there is one)
		}
		if (_highlightedSuggestionDiv) {					// do we have a highlighted address field now?
			addressSelected(_highlightedSuggestionDiv);			// yes - return the highlighted line
		}
	}
	hideCompleteDiv();
	_inputField.onblur=_blurHandler;
	if (_blurHandler) {								// does the caller want to handle this?
		_blurHandler(event);							// yes - let them do it all
	} else {									// the caller doesnt want to process
		_inputField.value = fullAddress(_inputField.item);			// just supply all the address concatenated
	}
	if (_mouseClicked && _nextControl)						// if selection was made by mouse and next control is specified
		_nextControl.focus();							// go to that control
}

function hideCompleteDiv()
//
//	Hide the list of addresses
{
	if (_completeDiv)
		_completeDiv.style.visibility="hidden";
}
function calculateOffsetLeft(control)
//
//	Calculate the total offset of the control from the left of the browser window.  This allows for controls embedded in other controls
{
	return nestedOffset(control,"offsetLeft")
}

function calculateOffsetTop(control)
//
//	Calculate the total offset of the control from the left of the browser window.  This allows for controls embedded in other controls
{
	return nestedOffset(control,"offsetTop")
}
function nestedOffset(control,attr)
//
//	Calculate the nested offset of the control from the left of the browser
{
	var totalOffset=0;
	while(control){
		totalOffset+=control[attr]; 
		control=control.offsetParent
	}
	return totalOffset
}
/*
function cacheResults(string, xml)
//
//	cache the XML string resulting from the previous query
{
	_previousLookups[string] = xml;							// save the data
}
*/
function setStyleForElement(c,name)
//
//	Set the style for this element to the named style.  We implement the styles internally, not using style sheets
{
	switch(name.charAt(0)){
		case "m":								// m-divContainer
			c.style.fontSize="11.1667px";
			c.style.fontFamily="arial,helvetica,sans-serif";
			c.style.wordWrap="break-word";
			c.style.textAlign="left";
			break;
		case "l":
			c.style.display="block";
			c.style.paddingLeft="3";
			c.style.paddingRight="3";
			c.style.height="16px";
			c.style.overflow="hidden";
			break;
		case "a":								// a-normalLineStyle
			c.style.backgroundColor="white";
			c.style.color="black";
			if(c.displaySpan){
				c.displaySpan.style.color="green"
			}
			break;
		case "b":								// b-highlightedLine
			c.style.backgroundColor="#3366cc";
			c.style.color="white";
			if(c.displaySpan){
				c.displaySpan.style.color="white"
			}
			break;
		case "c":
			c.style.width=p+"%";
			c.style.cssFloat="left";
			break;
		case "d":
			c.style.cssFloat="right";
			c.style.width=100-p+"%";
			c.style.fontSize="10px";
			c.style.textAlign="right";
			c.style.color="green";
			c.style.paddingTop="3px"
			break
	}
}

function displaySuggestedList(div, doc)
//
//	Display the matching addresses returned from the server
{
	if (XMLtext(doc, "type") == "nomatch") {					// Was there no match?
		displayError("The "+_searchType+" entered does not exist on our database.<br>Please re-check and re-enter the correct "+_searchType+".", _inputField);
		selectLastCharacter(_inputField);					// just select the last character typed
		return;
	}
	var width = calculateWidth(0);
	while(div.childNodes.length>0) {
	 div.removeChild(div.childNodes[0]);
	}
	_completeDivRows = -1;
	_completeDivDivList=null;							// clear the list of addresses
	// For each element in our list, we create:
	// <DIV (u) - mousedown/mouseover/mouseout a-normalLineStyle>
	// </DIV (u)>
	if (typeof doc != "object") {							// is this an object (XML document)
		hideCompleteDiv();							// no - hide the list
		return;									// and go away
	}
	var items = doc.getElementsByTagName(_searchType);				// extract all of what we are searching for
	if (items.length == 0) {							// Did we get any responses?
		hideCompleteDiv();							// no - hide the list
		return;									// and get out of here
	}
	div.style.overflowX = 'visible';
	div.style.width = '';
	showCompleteDiv();								// make the list visible

	for(var f=0; f<items.length; ++f){
		var u=document.createElement("DIV");
		setStyleForElement(u,"a-normalLineStyle");
		u.onmousedown=mousedownHandler;
		u.onmouseover=mouseoverHandler;
		u.onmouseout=mouseoutHandler;
		u.item = items[f];
		u.innerHTML = fullAddress(items[f]);
		if (!XMLfield(items[f], "address.addressid")) {
			u.style.backgroundColor="#ffff99";
		}
		div.appendChild(u)
		var fldWidth = u.offsetWidth;
		if (fldWidth > width)
			width = fldWidth;
	}
	div.style.overflowX='';
	div.style.width=calculateWidth(width+2)+"px"
	_completeDivDivList=_completeDiv.getElementsByTagName(_divTag);				// find the list of addresses
	_completeDivRows=_completeDivDivList.length;						// and the number of addresses
}
var mouseoverHandler=function()
//
//	mouse over - highlight the current control after un-highlighting any previous control
{
	if(_highlightedSuggestionDiv) {
		setStyleForElement(_highlightedSuggestionDiv,"a-normalLineStyle");
	}
	setStyleForElement(this,"b-highlightedLine")
}
;
var mouseoutHandler=function()
//
//	mouse out - un-highlight the current control
{
	setStyleForElement(this,"a-normalLineStyle")
}
function getElementText(elementName, XMLItem, index) 
//
//	retrieve text of an XML document element, including elements using namespaces
{
	var result = null;
	if (XMLItem) {
		result = XMLItem.getElementsByTagName(elementName)[index];
		if (result) {
			// get text, accounting for possible
			// whitespace (carriage return) text nodes 
			if (result.childNodes.length > 1) {
				return result.childNodes[1].nodeValue;
			} else {
				if (result.firstChild)
					return result.firstChild.nodeValue;	
			}
		}
  }
	return result;
}
function addressSelected(div)
//
//	The passed div contains the address that the user has selected
//	Update the nominated fields and pass the XML back
{
	_inputField.item = div.item.cloneNode(true);				// Put the XML returned into the control
	hideCompleteDiv();
}
var mousedownHandler=function()
//
//	onMouseDown function.  The mouse has been clicked on a div containing an address - select the address
//	because this also takes the focus from the input field the onblur function will be subsequently called
//	and this will extract the address.  We set the mouse clicked variable to let the onblur handler to
//	override control that focus moves to
{
	_mouseClicked = true;
	addressSelected(this);
}

function fullAddress(item)
//
//	Extract the full address from the xml item
{
	if (_searchType == "locality") {
		return(	  getElementText("suburb", item, 0) + ", " 
			+ getElementText("state", item, 0) + ", " 
			+ getElementText("postcode", item, 0));
	} else {	// (_searchType == "address")
		return(   getElementText("name", item, 0) + ", " 
			+ getElementText("suburb", item, 0) + ", " 
			+ getElementText("state", item, 0) + ", " 
			+ getElementText("postcode", item, 0));
	}
}
function localityID(field)
//
//	Extract the localityid from the xml item
{
	return(getElementText("localityid", field.item, 0));
}
function addressID(field)
//
//	Extract the addressid from the xml item
{
	return(getElementText("addressid", field.item, 0));
}
function highlightNewValue(lineNumber)
//
//	The up or down arrow has been pressed - select the new entry
{
	if(!_completeDivDivList || _completeDivRows<=0) {
		return;										// The address list is empty
	}
	showCompleteDiv();									// ensure the address list is visible
	if (lineNumber >= _completeDivRows) {							// if we are going past the end
		lineNumber = 0;									// wrap arounf to the start
	} else if (lineNumber<0) {								// if we are going past the beginning
		lineNumber = _completeDivRows-1;						// wrap arounf to the end
	}
	if (_highlightedSuggestionIndex != -1 && lineNumber != _highlightedSuggestionIndex) {	// If a different line was highlighted
		setStyleForElement(_highlightedSuggestionDiv,"a-normalLineStyle"); 		// un-highlight it
		_highlightedSuggestionIndex=-1
	}
	_highlightedSuggestionIndex = lineNumber;						// rememeber the highlighted line
	_highlightedSuggestionDiv = _completeDivDivList.item(lineNumber);			// and the DIV
	setStyleForElement(_highlightedSuggestionDiv,"b-highlightedLine");			// highlight it
}
