//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// SteveP's auto-suggest control

function clsAutoSuggestControl (sID, objAllItems) {

	// control unique ID - used in drop down ID name
	this.sID = sID;

	// the all-items list object
	this.objAllItems = objAllItems;

	// indexes of hidden items - default - no hidden items
	this.objHiddenItems = new Array();
	for (var i=0;  i<this.objAllItems[0].length;  i++)  this.objHiddenItems.push (false);

	// max number of suggestion items to display and suggestions to find
	this.iMaxNumDisplayItems    = 10;
	this.iMaxNumSuggestionItems = 99;

	// min number of characters before suggestions appear
	this.iMinNumCharacters = 1;

	// match only the start of the word
	this.bMatchStartOnly = true;

	// min height for each row and entire drop down; -1 = no min height
	this.iMinRowHeight      = -1;
	this.iMinDropDownHeight = -1;

	// widen the drop down a given pixel amount
	this.iWidenDropDown = 0;

	// widen the drop down if there's a scroll bar
	this.bWidenScrollbar = false;

	// default styling for the drop down
	this.fontFamily = 'verdana,arial,helvetica';
	this.fontSize   = '12px';
	this.border     = '1px solid black';
	this.backgroundColor = 'white';
	this.highlightColor  = '#D6D7E7';
	this.bkColorButton   = 'lightblue';
	this.bkColorHit      = 'lightyellow';

	// initialize the drop down suggestion object, "hit" count, "more"/"all"/"get" buttons
	this.objDropDown = null;
	this.objHit      = null;
	this.objMore     = null;
	this.objAll      = null;
	this.objGet      = null;

	// initialize the current suggestions object
	this.objCurrentSuggestions = new Array();

	// initialize the textbox and auto-fill objects
	this.objTextbox  = null;
	this.objAutoFill = new Array();

	// index numbers in the all-items list for matching, filling, displaying
	this.iSuggestionMatchIndex   = 0;
	this.iSuggestionFillIndex    = 0;
	this.iSuggestionDisplayIndex = 0;

	return this;

} //function - class

//------------------------------------------------------------------------------
// connect the auto suggestion control to the given textbox
// can only be connected to one textbox at a time

clsAutoSuggestControl.prototype.connectEntry = function (objTextbox) {

	// save this object for referencing in the handlers
	var objThis = this;

	// onblur delay
	// needs to be about 200-300 for IE (IE is slower)
	var iBlurDelay = 250;

	// show the "get" button if needed
	// (activeElement not supported in safari yet - HTML 5 standard)
	function showGet() {
		setTimeout (function() {
			if (document.activeElement)
				if (document.activeElement == objThis.objTextbox)
					objThis.placeGetButton();
				else {}
			else  // safari "buggy" support
				objThis.placeGetButton();
		}, 2*iBlurDelay);
	} //function

	// exit if already connected (re-show "get" button first)
	if (objTextbox == this.objTextbox) {
		showGet();
		return;
	} //if

	// disconnect first if needed, then connect
	this.disconnectEntry();
	this.objTextbox = objTextbox;

	// place the "get" button
	showGet();

	// handle blur - PROBLEMS- triggered when scrolling dropDown in IE,O,S (not FF)
	this.objTextbox.onblur = function (objEvent) {

		objEvent  = objEvent || window.event;
		objTarget = objEvent.target || objEvent.srcElement;

		// delay handling the blur event until the blur trigger catches up
		setTimeout (function() {
			if      (objThis.objDropDown.blurTrigger == 'dropDown_scroll') {
				objThis.objTextbox.focus();
				if (getCaretPosition (objThis.objTextbox) < 0)
					setCaretToEnd (objThis.objTextbox);
			} //if
			else if (objThis.objDropDown.blurTrigger == 'dropDown_click') {
				objThis.hideDropDown();
				setCaretToEnd (objThis.objTextbox);
			} //else if
			else {
				objThis.hideDropDown (true);
			} //else
			objThis.objDropDown.blurTrigger = '';
		}, iBlurDelay);  //delay in milliseconds

	} //function - event handler

	// stop propagated events for cursor movement callbacks (Opera only)
	this.objTextbox.onkeypress = function (objEvent) {

		if (!window.opera)  return;

		objEvent  = objEvent || window.event;
		objTarget = objEvent.target || objEvent.srcElement;

		var iKeyCode = objEvent.keyCode;

		switch (iKeyCode) {

			case 33: //page up
			case 34: //page down
			case 38: //arrow up
			case 40: //arrow down

				if (objThis.objDropDown.style.visibility == 'visible')
					return stopEvent(objEvent);
				break;

			default:;

		} //switch

	} //function - event handler


	// handle normal characters, DEL, BACKSPACE
	this.objTextbox.onkeyup = function (objEvent) {

		objEvent  = objEvent || window.event;
		objTarget = objEvent.target || objEvent.srcElement;

		var iKeyCode = objEvent.keyCode;

		// allow DEL and BACKSPACE

		if (iKeyCode < 7 ||
			(iKeyCode >=   9 && iKeyCode <=  31) ||
			(iKeyCode >=  33 && iKeyCode <=  45) ||
			(iKeyCode >= 112 && iKeyCode <= 123)) {
		//ignore
		} //if

		else {
			objThis.generateSuggestions (false, false);
			for (var i=0;  i<objThis.objAutoFill.length;  i++)
				with (objThis.objAutoFill[i])  if (!append)  object[field] = dvalue;
			if (getCaretPosition (objThis.objTextbox) < 0)
				setCaretToEnd (objThis.objTextbox);
		} //else

	} //function - event handler

	// handle up, down, enter/tab, esc
	this.objTextbox.onkeydown = function (objEvent) {

		objEvent  = objEvent || window.event;
		objTarget = objEvent.target || objEvent.srcElement;

		var iKeyCode = objEvent.keyCode;

		switch (iKeyCode) {

			case 45: //insert

				// exit if there's no drop down
				if (objThis.objDropDown.style.visibility == 'hidden')  return;

				// get more suggestions if available
				objThis.appendDropDownRows();
				return stopEvent (objEvent);
				break;

			case 33: //page up
			case 34: //page down
			case 38: //arrow up
			case 40: //arrow down

				// get the suggestions if needed, and determine the number of rows to go up/down
				var iRows = 0;
				if (objThis.objDropDown.style.visibility == 'visible')
					iRows = (iKeyCode == 33 || iKeyCode == 34) ? objThis.iMaxNumDisplayItems : 1;
				else
					objThis.generateSuggestions (false, false);

				// exit if there aren't any suggestions
				if (objThis.objCurrentSuggestions.length <= 0)  return;

				// get the table/tbody/row objects
				var objTable = objThis.objDropDown.firstChild;
				var objTbody = objTable.firstChild;

				// get the new selected index
				var iSelectedIndex = (iKeyCode == 33 || iKeyCode == 38)
					? Math.max (0,
						objThis.objDropDown.selectedIndex - iRows)
					: Math.min (
						objThis.objDropDown.selectedIndex + iRows,
						objTbody.rows.length - 1);

				// get the row
				var objRow = objTbody.rows[iSelectedIndex];

				// highlight the new row
				objThis.highlightDropDownItem (objRow, true);

				// initialize the height used in the "with" block below
				var iHeight = 0;

				// page/scroll up/down if needed - adjust for div border
				with (objThis.objDropDown) {
					iHeight = offsetHeight - (
						parseInt (style.borderTopWidth) +
						parseInt (style.borderBottomWidth));
					if (iKeyCode == 33)  scrollTop = Math.max (scrollTop - iHeight, 0);
					if (iKeyCode == 34)  scrollTop = Math.min (scrollTop + iHeight, objTable.offsetHeight - iHeight);
					if (scrollTop > objRow.offsetTop)
						scrollTop = objRow.offsetTop;
					if ((scrollTop + iHeight - objRow.offsetHeight) <= objRow.offsetTop)
						scrollTop = objRow.offsetTop - iHeight + objRow.offsetHeight;
				} //with

				if (getCaretPosition (objThis.objTextbox) < 0)
					setCaretToEnd (objThis.objTextbox);
				return stopEvent (objEvent);
				break;

			case 13: //enter
			case  9: //tab

				// exit if there's no drop down
				if (objThis.objDropDown.style.visibility == 'hidden')  return;

				// fill in the suggestion and auto-fills
				objThis.fillSuggestion();

				// hide the drop down and go to the textbox
				objThis.hideDropDown();
				setCaretToEnd (objThis.objTextbox);
				return stopEvent (objEvent);
				break;

			case 27: //esc - does't work in opera correctly

				// exit if there's no drop down
				if (objThis.objDropDown.style.visibility == 'hidden')  return;

				// don't suggest anything
				objThis.hideDropDown();
				if (getCaretPosition (objThis.objTextbox) < 0)
					setCaretToEnd (objThis.objTextbox);
				return stopEvent (objEvent);
				break;

			default:;

		} // switch

	} //function - event handler

} //function - method

//------------------------------------------------------------------------------
// disconnect the auto suggestion control from the textbox

clsAutoSuggestControl.prototype.disconnectEntry = function () {

	if (!this.objTextbox)  return;

	this.objTextbox.onblur     = null;
	this.objTextbox.onkeydown  = null;
	this.objTextbox.onkeypress = null;
	this.objTextbox.onkeyup    = null;
	this.objTextbox            = null;

} //function - method

//------------------------------------------------------------------------------
// connect the auto suggestion control to the given auto-fill objects

// variable length arguments - list of arrays - array indexes are:
//   0. object - object to be filled
//   1. field  - destination field to fill in the object
//   2. index  - source index in the item list used to fill
//   3. dvalue - default value if not auto-filled
//   4. append - append the value instead of replace

clsAutoSuggestControl.prototype.connectAutoFill = function () {

	// disconnect first
	this.disconnectAutoFill();

	// connect to each specified object
	for (var i=0;  i < arguments.length;  i++)

		this.objAutoFill.push ({
			"object" : arguments[i][0],
			"field"  : arguments[i][1],
			"index"  : arguments[i][2],
			"dvalue" : arguments[i][3],
			"append" : arguments[i][4]});

} //function - method

//------------------------------------------------------------------------------
// disconnect the auto suggestion control from all the auto-fill objects

clsAutoSuggestControl.prototype.disconnectAutoFill = function () {

	this.objAutoFill.splice (0, this.objAutoFill.length);

} //function - method

//------------------------------------------------------------------------------
// connect the auto suggestion control to both the textbox and auto-fill objects

clsAutoSuggestControl.prototype.connect = function (objTextbox) {

	// exit if already connected (place "get" button in connectEntry)
	if (objTextbox == this.objTextbox) {
		this.connectEntry (objTextbox);
		return;
	} //if

	this.connectEntry (objTextbox);

	// disconnect first
	this.disconnectAutoFill();

	// connect to each specified object
	for (var i=1;  i < arguments.length;  i++)

		this.objAutoFill.push ({
			"object" : arguments[i][0],
			"field"  : arguments[i][1],
			"index"  : arguments[i][2],
			"dvalue" : arguments[i][3],
			"append" : arguments[i][4]});

} //function - method

//------------------------------------------------------------------------------
// clear the suggestion and auto-fills

clsAutoSuggestControl.prototype.clearSuggestion = function () {

	this.objTextbox.value = '';

	for (var i=0;  i<this.objAutoFill.length;  i++)
		with (this.objAutoFill[i])
			object[field] = dvalue;

} //function - method

//------------------------------------------------------------------------------
// fill the suggestion and auto-fills

clsAutoSuggestControl.prototype.fillSuggestion = function () {

	this.objTextbox.value = this.objCurrentSuggestions[this.objDropDown.selectedIndex][this.iSuggestionFillIndex];

	for (var i=0;  i<this.objAutoFill.length;  i++)
		with (this.objAutoFill[i])
			object[field] = (append ? object[field] : '') +
				this.objCurrentSuggestions[this.objDropDown.selectedIndex][index];

} //function - method

//------------------------------------------------------------------------------
// create the drop down suggestion list object
// in IE, run this after the page loads or in body.onload

clsAutoSuggestControl.prototype.createDropDown = function () {

	// set a variable to point at "this" in the event handlers
	var objThis = this;

	//-------------------------
	// create the drop down div

	// set the name of the drop down id
	var sIdDropDown = 'div_dropDown_' + this.sID;

	// remove any drop down object/events if they already exist (stop memory leak)
	var div = document.getElementById (sIdDropDown);
	while (div) {
		div.onmousedown = null;
		document.body.removeChild (div);
		div = document.getElementById (sIdDropDown);
	} //while

	// create the drop down div
	this.objDropDown = document.createElement ('div');
	this.objDropDown.id = sIdDropDown;
	this.objDropDown.setAttribute ('selectedIndex', -1);
	with (this.objDropDown.style) {
		visibility = 'hidden';
		position   = 'absolute';
		overflow   = 'auto';
		overflowX  = 'hidden';
		zIndex     = 98;
		border     = this.border;
		fontFamily = this.fontFamily;
		backgroundColor = this.backgroundColor;
	} //with

	// set up the event handler for the scroll bar blur trigger
	this.objDropDown.setAttribute ('blurTrigger', '');
	this.objDropDown.onmousedown = function (objEvent) {

		// exit if this is firefox - textbox doesn't fire blur in FF when scrolling
		if (navigator.userAgent && (navigator.userAgent.indexOf('Firefox') != -1))  return;

		objEvent  = objEvent || window.event;
		objTarget = objEvent.target || objEvent.srcElement;

		// set the blur trigger to cancel the textbox blur event
		if (objTarget == this)
			objThis.objDropDown.blurTrigger = 'dropDown_scroll';

	}//funtion - event handler

	// attach the drop down to the document
	document.body.appendChild (this.objDropDown);

	//-----------------------
	// create the "hit" count

	// set the name of the "hit" count id
	var sIdHit = 'div_hit_' + this.sID;

	// remove any "hit" count object/events if they already exist (stop memory leak)
	var hit = document.getElementById (sIdHit);
	while (hit) {
		hit.onmousedown = null;
		document.body.removeChild (hit);
		hit = document.getElementById (sIdHit);
	} //while

	// create the "hit" count
	this.objHit = document.createElement ('div');
	this.objHit.id = sIdHit;
	with (this.objHit.style) {
		visibility = 'hidden';
		position   = 'absolute';
		zIndex     = '98';
		//marginLeft = '1px';
		fontSize   = '10px';
		border     = '1px solid black';
		padding    = '3px';
		fontFamily = this.fontFamily;
		backgroundColor = this.bkColorHit;
	} //with

	// disregard mouse clicks in the "hit" count
	this.objHit.onmousedown = function () {
		objThis.objDropDown.blurTrigger = 'dropDown_scroll';
	} //function - event handler

	// attach the "hit" count to the document
	document.body.appendChild (this.objHit);

	//-------------------------
	// create the "more" button

	// set the name of the "more" button id
	var sIdMore = 'btn_more_' + this.sID;

	// remove any "more" object/events if they already exist (stop memory leak)
	var more = document.getElementById (sIdMore);
	while (more) {
		more.onmousedown = more.onmouseup = null;
		document.body.removeChild (more);
		more = document.getElementById (sIdMore);
	} //while

	// create the "more" button
	this.objMore = document.createElement ('input');
	this.objMore.id    = sIdMore;
	this.objMore.type  = 'button';
	this.objMore.value = 'More';
	this.objMore.setAttribute ('iMore', 0);
	with (this.objMore.style) {
		visibility = 'hidden';
		position   = 'absolute';
		zIndex     = '98';
		width      = '48px';
		height     = '30px';
		//marginLeft = '1px';
		fontFamily = this.fontFamily;
		fontSize   = '10px';
		backgroundColor = this.bkColorButton;
	} //with

	// set up the event handlers for the "more" button click
	// use setTimeout calls to return the blurTrigger quickly
	this.objMore.onmousedown = function () {
		objThis.objDropDown.blurTrigger = 'dropDown_scroll';
	} //function - event handler
	this.objMore.onmouseup = function (objEvent) {
		objEvent = objEvent || window.event;
		if ((objEvent.button == 1) || (objEvent.which == 1)) {
			this.value = 'wait...';
			setTimeout (function() {
				objThis.appendDropDownRows(false, false);
				setCaretToEnd (objThis.objTextbox);
			}, 100);
		} //if
	} //function - event handler

	// attach the "more" button to the document
	document.body.appendChild (this.objMore);

	// set the name of the "all" button id
	var sIdAll = 'btn_all_' + this.sID;

	//------------------------
	// create the "all" button

	// remove any "all" object/events if they already exist (stop memory leak)
	var all = document.getElementById (sIdAll);
	while (all) {
		all.onmousedown = all.onmouseup = null;
		document.body.removeChild (all);
		all = document.getElementById (sIdAll);
	} //while

	// create the "all" button
	this.objAll = document.createElement ('input');
	this.objAll.id    = sIdAll;
	this.objAll.type  = 'button';
	this.objAll.value = 'All';
	with (this.objAll.style) {
		visibility = 'hidden';
		position   = 'absolute';
		zIndex     = '98';
		width      = '48px';
		height     = '30px';
		//marginLeft = '1px';
		fontSize   = '10px';
		fontFamily = this.fontFamily;
		backgroundColor = this.bkColorButton;
	} //with

	// set up the event handlers for the "all" button click
	// use setTimeouts call to return the blurTrigger quickly
	this.objAll.onmousedown = function () {
		objThis.objDropDown.blurTrigger = 'dropDown_scroll';
	} //function - event handler
	this.objAll.onmouseup = function (objEvent) {
		objEvent = objEvent || window.event;
		if ((objEvent.button == 1) || (objEvent.which == 1)) {
			objThis.objMore.style.visibility = 'hidden';
			this.value = 'wait...';
			setTimeout (function() {
				objThis.appendDropDownRows (false, true);
				setCaretToEnd (objThis.objTextbox);
			}, 100);
		} //if
	} //function - event handler

	// attach the "all" button to the document
	document.body.appendChild (this.objAll);

	//------------------------
	// create the "get" button

	// set the name of the "get" button id
	var sIdGet = 'btn_get_' + this.sID;

	// remove any "get" object/events if they already exist (stop memory leak)
	var get = document.getElementById (sIdGet);
	while (get) {
		get.onmousedown = get.onmouseup = null;
		document.body.removeChild (get);
		get = document.getElementById (sIdGet);
	} //while

	// create the "get" button
	this.objGet = document.createElement ('input');
	this.objGet.id    = sIdGet;
	this.objGet.type  = 'button';
	this.objGet.tabIndex = -1;
	this.objGet.value = '\u25BC';  // down unicode symbol
	with (this.objGet.style) {
		visibility = 'hidden';
		position   = 'absolute';
		zIndex     = '97';
		margin     = '1px';
		padding    = '0px';
		fontFamily = this.fontFamily;
		fontSize   = '8px';
		backgroundColor = this.bkColorButton;
	} //with

	// set up the event handlers for the "get" button click
	// use setTimeouts call to return the blurTrigger quickly
	this.objGet.onmousedown = function () {
		objThis.objDropDown.blurTrigger = 'dropDown_scroll';
	} //function - event handler
	this.objGet.onmouseup = function (objEvent) {
		objEvent = objEvent || window.event;
		if ((objEvent.button == 1) || (objEvent.which == 1)) {
		setTimeout (function() {
			if (objThis.objDropDown.style.visibility == 'visible') {
				objThis.hideDropDown();
			} //if
			else if (objThis.objTextbox.value == '')
				objThis.generateSuggestions (true, false);
			else {
				var temp = objThis.iMinNumCharacters;
				objThis.iMinNumCharacters = 1;
				objThis.generateSuggestions (false, false);
				objThis.iMinNumCharacters = temp;
			} //else
			if (getCaretPosition (objThis.objTextbox) < 0)
				setCaretToEnd (objThis.objTextbox);
		}, 0);
		} //if
	} //function - event handler

	// attach the "get" button to the document
	document.body.appendChild (this.objGet);

} //function - method

//------------------------------------------------------------------------------
// place the "get" button
clsAutoSuggestControl.prototype.placeGetButton = function () {

	with (this.objGet.style) {

		// position and size the button, then display it
		width = height = (this.objTextbox.offsetHeight - 2) + 'px';
		left  = (this.getLeft() + this.objTextbox.offsetWidth - this.objTextbox.offsetHeight) + 'px';
		top   = (this.getTop()) + 'px';
		visibility = 'visible';

	} //with

} //function - method

//------------------------------------------------------------------------------
// fill the drop down suggestion using a table element
// call this after finding the suggestions - must have at least 1 item

clsAutoSuggestControl.prototype.fillDropDown = function () {

	// clear the drop down
	this.objDropDown.innerHTML = '';
	this.objDropDown.selectedIndex = -1;

	// create the table/tbody
	// attach the tbody to the table and the table to the drop down
	var objTable = document.createElement ('table');
	this.objDropDown.appendChild (objTable);
	var objTbody = document.createElement ('tbody');
	objTable.appendChild (objTbody);

	// set the table style
	objTable.cellPadding = '3';
	with (objTable.style) {
		borderCollapse = 'collapse';
		fontFamily     = this.fontFamily;
		fontSize       = this.fontSize;
		cursor         = 'pointer';
		width          = '100%';
	} //with

	// to reference 'this' inside mouse handler
	var objThis = this;

	// mouse handlers for the drop down
	objTable.onmousedown =
	objTable.onmouseover =
	objTable.onmouseup =
	function (objEvent) {

		objEvent  = objEvent || window.event;
		objTarget = objEvent.target || objEvent.srcElement;

		// bubble up to the row
		// this is especially needed for embedded tags in the suggestions
		while ((objTarget.tagName != 'TR') && (objTarget.tagName != 'BODY'))
			objTarget = objTarget.parentNode;

		// hide the drop down if the row wasn't found or the mouse button was clicked
		if ((objEvent.type == 'mouseup') || (objTarget.tagName == 'BODY')) {
			if ((objEvent.button == 1) || (objEvent.which == 1)) {
				objThis.hideDropDown();
				setCaretToEnd (objThis.objTextbox);
			} //if
		} //if

		// fill the textbox and auto-fills, set the blur trigger for textbox blur handler
		// use a setTimeout call to return the blurTrigger quickly
		else if (objEvent.type == 'mousedown') {
			if ((objEvent.button == 1) || (objEvent.which == 1)) {
				objThis.objDropDown.blurTrigger = 'dropDown_click';
				setTimeout (function() { objThis.fillSuggestion(); }, 0);
			} //if
			else
				objThis.objDropDown.blurTrigger = 'dropDown_scroll';
		} //else

		// highlight the mouse-over row
		else if (objEvent.type == 'mouseover') {
			objThis.highlightDropDownItem (objTarget, true);
		} //else

		else {}

	} //function

	// append the initial suggestion drop down rows
	this.appendDropDownRows (true, false);

} //function - method

//------------------------------------------------------------------------------
// place the drop down (after it's initially filled)
clsAutoSuggestControl.prototype.placeDropDown = function () {

	// get the tbody object
	var objTbody = this.objDropDown.firstChild.firstChild;

	// maximize the width of the suggestion display column
	var iOffset = 0;
	for (var i=0;  i<this.objHiddenItems.length;  i++)
		if (i == this.iSuggestionDisplayIndex)  break;
		else if (this.objHiddenItems[i])  iOffset++;
		else {};
	objTbody.rows[0].cells[this.iSuggestionDisplayIndex - iOffset].style.width = '99%';

	// tally up the height
	var iHeight = 0;
	for (var i = 0;  i < this.objCurrentSuggestions.length;  i++)
		if (i < this.iMaxNumDisplayItems)
			iHeight += parseInt (objTbody.rows[i].style.height);
		else
			break;

	// adjust the height for the drop down min height
	iHeight = (iHeight > this.iMinDropDownHeight)
		? iHeight
		: this.iMinDropDownHeight;

	// set the width - widen if overflow
	var iWidth = this.objTextbox.offsetWidth;
	if (this.bWidenScrollbar && (this.objCurrentSuggestions.length > this.iMaxNumDisplayItems))
		iWidth += 18;  // less for Safari - diff at diff resolution

	// extend the width of the drop down per user setting
	iWidth += this.iWidenDropDown;

	with (this.objDropDown.style) {

		// adust width/height for IE div border
		if (document.all && !window.opera)
			iHeight += (parseInt(borderTopWidth)  + parseInt(borderBottomWidth));
		else
			iWidth  -= (parseInt(borderLeftWidth) + parseInt(borderRightWidth));

		// position the drop down
		left   = this.getLeft() + 'px';
		top    = (this.getTop() + this.objTextbox.offsetHeight) + 'px';
		width  = iWidth  + 'px';
		height = iHeight + 'px';

	} //with

	// highlight the first item and put the first item at the top
	this.highlightDropDownItem (objTbody.rows[0], true);
	this.objDropDown.scrollTop = objTbody.rows[0].offsetTop;

} //function - method

//------------------------------------------------------------------------------
// append the drop down suggestion with initial/more row items
// called initially and after user requests more suggestions
// provide whether this is the initial fill of the drop down

clsAutoSuggestControl.prototype.appendDropDownRows = function (bInit, bAll) {

	// get the tbody object
	var objTbody = this.objDropDown.firstChild.firstChild;

	// create each row
	this.objMore.iMore = 0;
	start_i = (bInit ? 0 : objTbody.rows.length);
	for (var i = start_i;  i < this.objCurrentSuggestions.length;  i++) {

		// quit if the max num suggestions are reached (unless ALL)
		if (!bAll && (i - start_i) >= this.iMaxNumSuggestionItems)  {
			this.objMore.iMore = this.objCurrentSuggestions.length - i;
			break;
		} //if

		// create the row
		var objRow = document.createElement ('tr');
		objTbody.appendChild (objRow);

		// create each cell
		for (var j=0;  j < this.objCurrentSuggestions[i].length;  j++) {

			// don't add items hidden items
			if (this.objHiddenItems[j])  continue;

			// create the cell
			var objCell = document.createElement ('td');
			objRow.appendChild (objCell);

			// don't allow word wrapping by default
			if (j != this.iSuggestionDisplayIndex)
				objCell.style.whiteSpace = 'nowrap';

			// set the contents of the cell
			objCell.innerHTML = this.objCurrentSuggestions[i][j];

		} //for j

		// adjust the height to the minimum if needed
		// need to set at least one cell for Safari
		objRow.cells[0].style.height = Math.max (objRow.offsetHeight, this.iMinRowHeight) + 'px';
		objRow.style.height          = Math.max (objRow.offsetHeight, this.iMinRowHeight) + 'px';

	} //for i

	// place the drop down
	if (bInit)  this.placeDropDown();

	with (this.objDropDown.style) {

		// initialize the top/left of the "hit" count and "more"/"all" buttons
		var iTop  = 0;
		var iLeft = 0;

		// adust left for non-IE div borders
		if (document.all && !window.opera) {}
		else  iLeft += (parseInt(borderLeftWidth) + parseInt(borderRightWidth ));

		// get the right position of the drop down (to place "hit" count and buttons)
		iLeft += parseInt(left) + parseInt(width);

		// position to "hit" count
		this.objHit.style.top  = parseInt(top) + iTop  + 'px';
		this.objHit.style.left =                 iLeft + 'px';

		// if needed, position the "more" and "all" buttons
		if (this.objMore.iMore > 0) {

			// adust top for non-IE div borders
			if (document.all && !window.opera) {}
			else  iTop  += (parseInt(borderTopWidth) + parseInt(borderBottomWidth));

			// get the lower-right position of the drop down (to place buttons)
			iTop += parseInt(top) + parseInt(height);

			// adust for the height/width of the "all" button
			iTop = iTop - parseInt (this.objAll.style.height);

			// position to "all" button
			this.objAll.style.top  = iTop  + 'px';
			this.objAll.style.left = iLeft + 'px';

			// adust for the height/width of the "more" button
			iTop = iTop - parseInt (this.objMore.style.height);

			// position to "more" button
			this.objMore.style.top  = iTop  + 'px';
			this.objMore.style.left = iLeft + 'px';

		} //if

	} //with

	// set the button and count labels
	this.objHit.innerHTML = (this.objCurrentSuggestions.length - this.objMore.iMore) + '/' + this.objCurrentSuggestions.length;
	this.objMore.value = '+' + Math.min (this.objMore.iMore, this.iMaxNumSuggestionItems);
	this.objAll .value = '+' + this.objMore.iMore;

	this.objHit .style.visibility = 'visible';
	this.objMore.style.visibility = ((this.objMore.iMore > this.iMaxNumSuggestionItems) ? 'visible' : 'hidden');
	this.objAll .style.visibility = ((this.objMore.iMore > 0) ? 'visible' : 'hidden');

} //function - method

//------------------------------------------------------------------------------
// show the drop down suggestion list

clsAutoSuggestControl.prototype.showDropDown = function () {

	this.objDropDown.style.visibility = 'visible';
	this.objHit     .style.visibility = 'visible';
	this.objMore.style.visibility = ((this.objMore.iMore > this.iMaxNumSuggestionItems) ? 'visible' : 'hidden');
	this.objAll .style.visibility = ((this.objMore.iMore > 0) ? 'visible' : 'hidden');

	//scroll up if needed to show the entire drop down - different for IE, Opera
	var iDropDownBottomPx = this.objDropDown.offsetTop + this.objDropDown.offsetHeight;
	var iBodyBottomPx = ((document.all || window.opera)
		? (document.body.clientHeight + document.body.scrollTop)
		: (window.innerHeight + window.scrollY));
	var iUndisplayedPx = iDropDownBottomPx - iBodyBottomPx;
	if (iUndisplayedPx > 0)  window.scrollBy (0, iUndisplayedPx);

} //function - method

//------------------------------------------------------------------------------
// hide the drop down suggestion list

clsAutoSuggestControl.prototype.hideDropDown = function (bHideGet) {

	if (bHideGet)  this.objGet.style.visibility = 'hidden';

	this.objDropDown.style.visibility = 'hidden';
	this.objHit     .style.visibility = 'hidden';
	this.objMore    .style.visibility = 'hidden';
	this.objAll     .style.visibility = 'hidden';

} //function - method

//------------------------------------------------------------------------------
// highlight (or not) the given object (assume a row object is passed in)

clsAutoSuggestControl.prototype.highlightDropDownItem = function (objRow, bHighlight) {

	if (bHighlight) {

		// unhighlight the previous selection
		if (this.objDropDown.selectedIndex > -1) {

			var objTable = objRow.parentNode;
			var objOldRow = objTable.rows[this.objDropDown.selectedIndex];
			objOldRow.style.backgroundColor = this.backgroundColor;

		} //if

		objRow.style.backgroundColor = this.highlightColor;
		this.objDropDown.selectedIndex = objRow.rowIndex;

	} //if

	else {

		objRow.style.backgroundColor = this.backgroundColor;
		this.objDropDown.selectedIndex = -1;

	} //else

} //function - method

//------------------------------------------------------------------------------
// find the suggestions based on the current text or get all items if desired

clsAutoSuggestControl.prototype.findSuggestions = function (bGetAllItems, bNarrowSearch) {

	// just return a cloned copy if all items are desired
	if (bGetAllItems) {

		// just return the current list
		if (bNarrowSearch)  return;

		// clear the current suggestion list
		this.objCurrentSuggestions.splice (0, this.objCurrentSuggestions.length);

		for (var i=0;  i<this.objAllItems.length;  i++)
			this.objCurrentSuggestions.push (this.objAllItems[i]);
		return;
	} //if

	// get the current value in the text box to search the item list below
	var sText = this.objTextbox.value.toLowerCase();

	// do a narrow search - only search the current list - remove non-matches
	if (bNarrowSearch) {

		// if there's nothing in the textbox, clear the current suggestion list
		if (!sText || (sText.length < this.iMinNumCharacters)) {
			this.objCurrentSuggestions.splice (0, this.objCurrentSuggestions.length);
			return;
		} //if

		for (var i=this.objCurrentSuggestions.length-1;  i>=0;  i--) {

			// get the item being searched
			var sItem = this.objCurrentSuggestions[i][this.iSuggestionMatchIndex].toLowerCase();

			// only remove unmatched items
			var bMatch = (this.bMatchStartOnly ? (sItem.indexOf(sText) == 0) : (sItem.indexOf(sText) >= 0));
			if (!bMatch)  this.objCurrentSuggestions.splice (i, 1);

		} //for

	} //if

	// do a wide search of the entire item list - include matches
	else {

		// clear the current suggestion list
		this.objCurrentSuggestions.splice (0, this.objCurrentSuggestions.length);

		// exit if there's nothing in the textbox
		if (!sText || (sText.length < this.iMinNumCharacters))  return;

		// go through all the items and find matches
		for (var i=0;  i<this.objAllItems.length;  i++) {

			// get the item being searched
			var sItem = this.objAllItems[i][this.iSuggestionMatchIndex].toLowerCase();

			// only push matched items
			var bMatch = (this.bMatchStartOnly ? (sItem.indexOf(sText) == 0) : (sItem.indexOf(sText) >= 0));
			if (bMatch)  this.objCurrentSuggestions.push (this.objAllItems[i]);

		} //for

	} //else

} //function - method

//------------------------------------------------------------------------------
// get the absolute left x coord of the given DOM node object

clsAutoSuggestControl.prototype.getLeft = function() {

	var objNode = this.objTextbox;
	var iLeft   = 0;

	// adjust top/left for IE, FF, safari (no adjustment for opera needed)
	var is_msie = (document.all && !window.opera) ? true : false;
	var is_firefox = ((navigator.userAgent) && (navigator.userAgent.indexOf('Firefox') != -1)) ? true : false;
	var is_safari = ((navigator.vendor) && (navigator.vendor.indexOf('Apple') != -1)) ? true : false;

	// bubble up to the body node
	while (objNode.tagName != 'BODY') {

		// adjust for IE (only count DIV borders)
		if (is_msie && objNode.tagName == 'DIV') {
			var iWidth = parseInt (objNode.style.borderLeftWidth);
			iLeft += (isNaN (iWidth) ? 0 : iWidth);
		} //if

		iLeft  += objNode.offsetLeft;
		objNode = objNode.offsetParent;

		// adjust for safari borders (place here to not include textbox border)
		if (is_safari) {
			var iWidth = parseInt (objNode.style.borderLeftWidth);
//			iLeft += (isNaN (iWidth) ? 0 : iWidth);
//decklist hack
iLeft += (isNaN (iWidth) ? 0 : (typeof JR_yr == "undefined" ? iWidth : iWidth/2));
		} //if

	} //while

	// adjust for FF (needs BODY border)
	if (is_firefox) {
		var iWidth = parseInt (objNode.style.borderLeftWidth);
		iLeft += (isNaN (iWidth) ? 0 : iWidth);
	} //if

	return iLeft;

}  //function - method

//------------------------------------------------------------------------------
// get the absolute top y coord of the given DOM node object

clsAutoSuggestControl.prototype.getTop = function() {

	var objNode = this.objTextbox;
	var iTop    = 0;

	// adjust top/left for IE, FF, safari (no adjustment for opera needed)
	var is_msie    = (document.all && !window.opera) ? true : false;
	var is_firefox = ((navigator.userAgent) && (navigator.userAgent.indexOf('Firefox') != -1)) ? true : false;
	var is_safari  = ((navigator.vendor) && (navigator.vendor.indexOf('Apple') != -1)) ? true : false;

	// bubble up to the body node
	while (objNode.tagName != 'BODY') {

		// adjust for IE (only count DIV borders)
		if (is_msie && objNode.tagName == 'DIV') {
			var iWidth = parseInt (objNode.style.borderTopWidth);
			iTop += (isNaN (iWidth) ? 0 : iWidth);
		} //if

		iTop   += objNode.offsetTop;
		objNode = objNode.offsetParent;

		// adjust for safari borders (place here to not include textbox border)
		if (is_safari) {
			var iWidth = parseInt (objNode.style.borderTopWidth);
//			iTop += (isNaN (iWidth) ? 0 : iWidth);
//decklist hack
iTop += (isNaN (iWidth) ? 0 : (typeof JR_yr == "undefined" ? iWidth : iWidth/2));
		} //if

	} //while

	// adjust for FF (needs BODY border)
	if (is_firefox) {
		var iWidth = parseInt (objNode.style.borderTopWidth);
		iTop += (isNaN (iWidth) ? 0 : iWidth);
	} //if

	return iTop;

}  //function - method

//------------------------------------------------------------------------------
// start showing suggestions

clsAutoSuggestControl.prototype.generateSuggestions = function (bGetAllItems, bNarrowSearch) {

	// find suggestions and end if there aren't any
	this.findSuggestions (bGetAllItems, bNarrowSearch);
	if (this.objCurrentSuggestions.length == 0) {
		this.hideDropDown();
		return;
	} //if

	// fill and show the suggestion drop down
	this.fillDropDown();
	this.showDropDown();

} //function - method

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// utility functions

//------------------------------------------------------------------------------
// clone the self-referencing object - good for deep copying arrays
/*
Object.prototype.clone = function() {

	var newObj = (this instanceof Array) ? [] : {};
	for (i in this) {
		if (i == 'clone')  continue;
		if (this[i] && typeof this[i] == "object")
			newObj[i] = this[i].clone();
		else
			newObj[i] = this[i];
	} //for
	return newObj;

} //function - extension of Object
*/
//------------------------------------------------------------------------------
// put the text caret at the end of the text in the text field control

function setCaretToEnd (control) {

	if (control.setSelectionRange) {
		control.focus();
		var length = control.value.length;
		control.setSelectionRange(length, length);
	} //if
	else if (control.createTextRange) {
		var range = control.createTextRange();
		range.collapse(false);
		range.select();
	} //else if

} //function

//------------------------------------------------------------------------------
// put the text caret at the start of the text in the text field control

function setCaretToStart (control) {

	if (control.setSelectionRange) {
		control.focus();
		control.setSelectionRange(0, 0);
	} //if
	else if (control.createTextRange) {
		var range = control.createTextRange();
		range.collapse(true);
		range.select();
	} //else if

} //function

//------------------------------------------------------------------------------
// sets the caret position to pos in the text field control

function setCaret (control, pos) {

	control.focus();

	if (control.setSelectionRange) {
		control.setSelectionRange (pos, pos);
	} //if
	else if (control.createTextRange) {
		var range = control.createTextRange();		
		range.moveStart ('character', pos);
		range.collapse();
		range.select();
	} //else if

} //function

//------------------------------------------------------------------------------
// get the caret position in the given control

function getCaretPosition (ctrl) {

	var CaretPos = -1;

	// IE Support
	if (document.selection) {
		ctrl.focus ();
		var Sel = document.selection.createRange ();
		Sel.moveStart ('character', -ctrl.value.length);
		CaretPos = Sel.text.length;
	} //if

	// Firefox support
	else if (ctrl.selectionStart || ctrl.selectionStart == '0')
		CaretPos = ctrl.selectionStart;

	return (CaretPos);

} //function

//------------------------------------------------------------------------------
// Stop an event from bubbling up the event DOM

function stopEvent (objEvent)
{
	objEvent  = objEvent || window.event;

	if (objEvent) {

		if (objEvent.stopPropagation)  objEvent.stopPropagation();
		if (objEvent.preventDefault)   objEvent.preventDefault();

		if (typeof objEvent.cancelBubble != "undefined") {

			objEvent.cancelBubble = true;
			objEvent.returnValue  = false;

		} //if
	} //if

	return false;

} //function
