
function ComboBox(sObjName, sID, sClassName, sInitialValue, iPerPage, iItemHeight, aList)
{
	this.ID = sID;
	this.Handle = sObjName;
	this.List = new Array();
	this.LastCompareName = '';	
	this.StyleClass = (sClassName)? sClassName : "Primary";
	this.InitialValue = (sInitialValue)? sInitialValue : "";
	this.LinkHeight = (iItemHeight)? iItemHeight : 15;
	this.PageLength = (iPerPage)? iPerPage : 15;	
	this.listShowing = false;
	this.hideTimeout = null;
	this.Initial = true;
	
	if(typeof aList != "undefined") this.addItem(aList);

	//these properties get set when the object
	//is bound to an element in the page.
	this.WidthPx;
	this.lDiv;
	this.buDiv;
	this.bdDiv;
	this.tDiv;
	this.iFrame;
}

/************************************************
METHOD: AddItem (overloaded)
PRECONDITION: Can recieve 1 of 2 inputs.
	1) a single instance of the 'BoxItem' object
	2) an array of 'BoxItem' objects
************************************************/
ComboBox.prototype.addItem = function(obj)
{
	if(obj instanceof Array)
	{
		for(var i=0; i<obj.length; i++)
				this.List.push(new BoxItem(obj[i].ID, obj[i].Name, obj[i].URL, this));
	}
	else if(obj instanceof Object)
		this.List.push(new BoxItem(obj.ID, obj.Name, obj.URL, this));
}


ComboBox.prototype.BindDisplay = function(sBindingTagID)
{
	var inner;
		
	//add a reference to the bound element and this instance
	//of the object to the global array of all boxes.
	ComboBox.BOXES.push([sBindingTagID, this.ID, this.Handle]);

	this.WidthPx = parseInt(GetTag(sBindingTagID).style.width);
	
	inner = '<div align="center" onblur="' + this.Handle + '.SetHide();" onfocus="' + this.Handle + '.UnSetHide();" class="' + this.StyleClass + ' UpDownLink" onMouseDown="' + this.Handle + '.startScroll(true);" onMouseUp="' + this.Handle + '.endScroll();" onMouseOut="' + this.Handle + '.endScroll();" ID="COMBOBOX:' + this.Handle + ':UPSCROLL" style="display:none; position:absolute; width:' + this.WidthPx.toString() + 'px; height:15px;z-index:5;"><img src="/vlImages/up.gif"></div>';
	inner += '<div align="left" onBlur="' + this.Handle + '.SetHide();" onfocus="' + this.Handle + '.UnSetHide();" ID="COMBOBOX:' + this.Handle + ':LIST" style="display:none; position:absolute; width:' + this.WidthPx + 'px; z-index:5;" class="' + this.StyleClass + ' ComboBoxList"></div>';
	inner += '<div align="center" onblur="' + this.Handle + '.SetHide();" onfocus="' + this.Handle + '.UnSetHide();" class="' + this.StyleClass + ' UpDownLink" onMouseDown="' + this.Handle + '.startScroll(false);" onMouseUp="' + this.Handle + '.endScroll();" onMouseOut="' + this.Handle + '.endScroll();" ID="COMBOBOX:' + this.Handle + ':DOWNSCROLL" style="display:none; position:absolute; width:' + this.WidthPx.toString() + 'px; height:15px;z-index:5;"><img src="/vlImages/down.gif"/></div>';
	
	if(document.all)
		inner += '<iframe src="about:blank" style="position:absolute;width:0;height:0;" frameborder=0 border=0 scrolling=no zindex=0 ID="COMBOBOX:' + this.Handle + ':IFRAME"></iframe>';
	
	GetTag(sBindingTagID).parentNode.innerHTML += inner;
	
	var InputField = GetTag(sBindingTagID);
	
	InputField.value = this.InitialValue;
	
	addEvent(InputField, 'blur', ComboBox.BlurHandler);
	addEvent(InputField, 'focus', ComboBox.FocusHandler);
	addEvent(InputField, 'keydown', ComboBox.KeyDownHandler);
	addEvent(InputField, 'keyup', ComboBox.KeyUpHandler);
	
	this.tDiv = InputField;
	this.lDiv = GetTag('COMBOBOX:' + this.Handle + ':LIST');
	this.buDiv = GetTag('COMBOBOX:' + this.Handle + ':UPSCROLL');
	this.bdDiv = GetTag('COMBOBOX:' + this.Handle + ':DOWNSCROLL');
	
	if(document.all)
		this.iFrame = GetTag('COMBOBOX:' + this.Handle + ':IFRAME');
}

ComboBox.prototype.SetHide = function()
{
	if(!this.hideTimeout)
		this.hideTimeout = setTimeout(this.Handle + '.HideDropDown()', 500);
}

ComboBox.prototype.UnSetHide = function()
{
	if(this.hideTimeout)
	{
		clearTimeout(this.hideTimeout);
		this.hideTimeout = null;
	}
}

ComboBox.prototype.onKeyPress = function()
{
	this.DrawDropDown();
}

ComboBox.prototype.onTextBoxKeyDown = function()
{
	try {
		this.DrawDropDown();
		this.FocusOption(0);
	} catch(e){ alert(e.message); }
}

ComboBox.prototype.onButtonPress = function()
{
	if(this.listShowing){
		this.HideDropDown();
	}else {
		this.DrawDropDown();
		try{
			GetTag('COMBOBOX:' + this.Handle + ':OPTION:0').focus();
		}catch(e){}	
	}
}

ComboBox.prototype.HideDropDown = function(){
		try {
			this.buDiv.style.display = 'none';
			this.bdDiv.style.display = 'none';
			this.lDiv.style.display = 'none';
			if(document.all)
				this.iFrame.style.display = 'none';
			this.listShowing = false;
		}catch(e){
			alert('error in HideDropDown() - ' + e.message);
		}
}


ComboBox.prototype.putIntoRange = function(index){

	if (index >= this.ListTop && index <= this.ListBottom){
		return true;
	}else{
		if(index < this.ListTop){
			this.PushLink();
			this.RedrawContents();
		}else{
			this.PopLink();
			this.RedrawContents();
		}
	return false;
	}

}

ComboBox.prototype.FocusOption = function(index)
{
	this.CurrentPosition = index;
	GetTag('COMBOBOX:' + this.Handle + ':OPTION:' + index).focus();
}

ComboBox.prototype.onListKeyDown = function(newIndex)
{
	oldTop = document.body.scrollTop;
	
	if(newIndex == (this.CurrentList.length))
		return null;
	
	this.putIntoRange(newIndex);
	this.FocusOption(newIndex);
	
	if(!document.all)
		setTimeout('if(document.body.scrollTop != ' +oldTop + ')document.body.scrollTop = ' +oldTop + ';', 0);
}

ComboBox.prototype.onListKeyUp = function(newIndex)
{
	this.putIntoRange(newIndex);
	this.FocusOption(newIndex);
}


ComboBox.prototype.PopLink = function()
{
	if(this.ListBottom < this.CurrentList.length - 1)
	{
		this.ListBottom += 1;
		this.ListTop += 1;
	}
}

ComboBox.prototype.PushLink = function()
{
	if(this.ListTop != 0)
	{
		this.ListBottom -= 1;
		this.ListTop -= 1;
	}
}

ComboBox.prototype.ScrollMenu = function(isUp)
{		
	if(isUp) {
		if(this.CurrentPosition > 0)
			this.onListKeyUp(this.CurrentPosition - 1);
	} else {
		this.onListKeyDown(this.CurrentPosition + 1);
	}
	
	switch(this.scrollCount)
	{
		case 15:
			clearInterval(this.scrollInterval);
			this.scrollInterval = setInterval(this.Handle + '.ScrollMenu(' + isUp.toString() + ');', 35);
		break;
		case 30:
			clearInterval(this.scrollInterval);
			this.scrollInterval = setInterval(this.Handle + '.ScrollMenu(' + isUp.toString() + ');', 10);	
		break;
		case 60:
			clearInterval(this.scrollInterval);
			this.scrollInterval = setInterval(this.Handle + '.ScrollMenu(' + isUp.toString() + ');', 3);
		break;	
	}
	
	this.scrollCount++;
}

ComboBox.prototype.startScroll = function(isUp)
{
	this.scrollCount = 0;
	if(isUp)
		this.FocusOption(this.ListTop);
	else
		this.FocusOption(this.ListBottom);
	
	this.scrollInterval = setInterval(this.Handle + '.ScrollMenu(' + isUp.toString() + ');', 75);
}

ComboBox.prototype.endScroll = function()
{
	clearInterval(this.scrollInterval);
}

ComboBox.prototype.RedrawContents = function()
{
	var out = '';
	
	for(var i = this.ListTop; i <= this.ListBottom;i++)
		out += this.CurrentList[i];
	
	this.lDiv.innerHTML = out;
	var dTag = this.lDiv;
	var tag = this.tDiv;
	var iHeight;
		
	var top = getAbsoluteTop(tag);
	var left = getAbsoluteLeft(tag);
	
	dTag.style.overflow = 'hidden';
	dTag.style.top = top + tag.offsetHeight;
	if(this.CurrentList.length < this.PageLength)
	{	
		dTag.style.height = iHeight = (this.CurrentList.length * this.LinkHeight) + "px";
				
		dTag.style.left = left;
		dTag.style.top = top + tag.offsetHeight;
		dTag.style.display = 'block';
		this.buDiv.style.display = 'none';
		this.bdDiv.style.display = 'none';
	}
	else 
	{
		dTag.style.height = iHeight = (this.PageLength * this.LinkHeight) + "px";
	
		dTag.style.left = left;
		dTag.style.display = 'block';

		this.buDiv.style.top = this.lDiv.style.top;
		this.buDiv.style.left = this.lDiv.style.left;
		this.buDiv.style.display = 'block';
		dTag.style.top = top + tag.offsetHeight + this.buDiv.offsetHeight;
		
		this.bdDiv.style.top = getAbsoluteTop(dTag) + dTag.offsetHeight;
		this.bdDiv.style.left = this.lDiv.style.left;
		this.bdDiv.style.display = 'block';
	}
	
	this.listShowing = true;
	
	if(this.CurrentList.length > this.PageLength){
		if(this.ListTop > 0){
			this.buDiv.innerHTML = '<img src="/vlImages/up.gif">';
		}else{
			this.buDiv.innerHTML = '<img src="/vlImages/up_grey.gif">';
		}
		
		if(this.ListBottom < (this.CurrentList.length - 1)){
			this.bdDiv.innerHTML = '<img src="/vlImages/down.gif">';
		}else{
			this.bdDiv.innerHTML = '<img src="/vlImages/down_grey.gif">';
		}	
	}
	
	if(this.iFrame)
	{
		this.iFrame.style.left = left;
		this.iFrame.style.top = top + tag.offsetHeight;
		this.iFrame.style.width = dTag.style.width.fromPx();
		this.iFrame.style.height = iHeight;
		this.iFrame.style.display = 'block';
		/*alert(this.iFrame.style.top);
		alert(this.iFrame.style.left);
		alert(this.iFrame.style.width);
		alert(this.iFrame.style.left);*/
	}
}

ComboBox.prototype.DrawDropDown = function(mustShow)
{	
	var txtDiv = this.tDiv;
	var out =  this.List.GetComboList(txtDiv.value, this, mustShow);
	
	if (out.length == 0) return true;

	this.CurrentList = out;
	this.CurrentPosition = 0;
	this.ListTop = 0;
	this.ListBottom = (this.CurrentList.length >= 14)?14:(this.CurrentList.length - 1);
	
	this.RedrawContents();
}

ComboBox.prototype.goComboLink = function()
{
	if(this.LastList)
	{
	    if(this.LastList.length)
	    {
            window.location.href = this.LastList[0].URL;	
	    }
	}else
	{
	    alert('You must type at least a partial name before attempting to search.');
	}
	
}

ComboBox.BOXES = new Array();

ComboBox.getObjHandle = function(sID)
{
    for(var i=0; i<ComboBox.BOXES.length; i++)
        if(ComboBox.BOXES[i][0] == sID) {
            var objBoxHandle = ComboBox.BOXES[i][2];
            break;
        }
    return eval(objBoxHandle);
}

ComboBox.BlurHandler = function(e)
{
	var target = window.event ? event.srcElement : e ? e.target : null;
	ComboBox.getObjHandle(target.id).SetHide();
}

ComboBox.FocusHandler = function(e)
{ 
	var target = window.event ? event.srcElement : e ? e.target : null;
	var CBox = ComboBox.getObjHandle(target.id);
	CBox.UnSetHide();
	
	if(CBox.Initial)
	{
		target.value = '';
		CBox.Initial = false;
	}
}

ComboBox.KeyDownHandler = function(e)
{
	var target = window.event ? event.srcElement : e ? e.target : null;
	var CBox = ComboBox.getObjHandle(target.id);
	e = e ? e : window.event;
	
	if(e.keyCode == 13)
		setTimeout(CBox.Handle + '.goComboLink()', 1);
	
	if(e.keyCode == 40)
		setTimeout(CBox.Handle + '.onTextBoxKeyDown()', 1);
		
	return true;
}

ComboBox.KeyUpHandler = function(e)
{
	var target = window.event ? event.srcElement : e ? e.target : null;
	var CBox = ComboBox.getObjHandle(target.id);
	setTimeout(CBox.Handle + '.onKeyPress();', 1);
	return true;
}



/*================================================
	BOX ITEM CLASS DEFINITION
================================================*/

// 'BoxItem' object Definition

function BoxItem(iID, sName, sURL, oComboBox)
{
	this.ID = iID;
	this.Name = sName;
	this.aName = sName.trim().split(' ');
	this.firstName = this.aName[0].toString().toLowerCase();
	this.lastName = this.aName[this.aName.length - 1].toString().toLowerCase();
	this.URL = sURL;
	this.Sound = sName.SoundString(false);
	this.Style = oComboBox.StyleClass;
	this.BoxHandle = oComboBox.Handle;
	this.uCaseName = sName.CompareString();
	
	if(document.all)
		this.Height = "height:" + oComboBox.LinkHeight + "px;";
	else
		this.Height = "min-height:" + oComboBox.LinkHeight + "px;";
}

BoxItem.prototype.GetComboLink = function(iIndex)
{
    var out;
    var url = this.URL;
    
    out = '<a ID="COMBOBOX:' + this.BoxHandle + ':OPTION:' + iIndex + '" onblur="' + this.BoxHandle + '.SetHide();" ';
    out += 'onfocus="' + this.BoxHandle + '.UnSetHide();"  onkeydown="BoxItem.KeyHandler(event, ' + this.BoxHandle + ', ' + iIndex + ');" class="' + this.Style + ' ListItem" ';
	out += 'style="display:block;' + this.Height + '" onkeypress="BoxItem.KeyHandler(event, ' + this.BoxHandle + ', ' + iIndex + ');" href="' + url + '">' + this.Name + '</a>';
	
	return out;
}

BoxItem.prototype.SoundsLike = function(sSound)
{
    var aName = this.Sound.split(' ');
    var sFirst = aName[0];
    var sLast = aName[aName.length - 1];
    var aSound = sSound.split(' ');
    var sSoundFirst = aSound[0].toString();
    var sSoundLast = aSound[aSound.length - 1].toString();
	if (this.Sound.length < sSound.length){
		return false;
	}
	else
	{
	    switch(aSound.length)
	    {
	        case 0:
	            if(sFirst.substring(0, sSoundFirst.length) == sSoundFirst || sLast.substring(0, sSoundFirst.length)== sSoundFirst)
			        return true;
            default:
                if(sFirst.substring(0, sSoundFirst.length) == sSoundFirst || sLast.substring(0, sSoundLast.length) == sSoundLast)
                    return true;
		}
	}
	return false;
}

BoxItem.prototype.toString = function() {
	return (this.Name + ' [' + this.Sound + ']');
}

BoxItem.KeyHandler = function(e, oComboBox, iIdx)
{	
	if(e.keyCode == 13) return true;
	
	if(e.keyCode == 40)
	{
		try {
			oComboBox.onListKeyDown(iIdx + 1);
		}
		catch(err){}
	} else {
		if(e.keyCode == 38)
		{
			try {
				if(iIdx == 0)
					oComboBox.tDiv.focus();
				else
					oComboBox.onListKeyUp(iIdx - 1);
			} catch(err) {
				alert('error in BoxItem.onKeyDown handler: ' + err.message);
			}
		}
	}
	return false;
}
BoxItem.prototype.isPartialMatch = function(sVal)
{
	var aTypedName = sVal.toString().trim().split(' ');
	var sTypedFirstName = aTypedName[0].toString().toLowerCase().trim();
	var sTypedLastName = aTypedName[aTypedName.length - 1].toString().toLowerCase().trim();
    switch(aTypedName.length)
    {
        case 0:
            if(this.firstName == sTypedFirstName || this.lastName == sTypedFirstName)
                return true;
            break;
        default:
            if(this.firstName == sTypedFirstName || this.lastName == sTypedLastName)
                return true;
            break;
    }
    return false;
}
BoxItem.prototype.isTotalMatch = function(sVal)
{
	var aTypedName = sVal.toString().trim().split(' ');
	var sTypedFirstName = aTypedName[0].toString().toLowerCase().trim();
	var sTypedLastName = aTypedName[aTypedName.length - 1].toString().toLowerCase().trim();
    switch(aTypedName.length)
    {
        case 0:
                return false;
        default:
            if(this.firstName == sTypedFirstName && this.lastName.substring(0, sTypedLastName.length) == sTypedLastName)
                return true;
            break;
    }
    return false;
}
/*=====================================================
	Base [JS] Object Prototype Extentions
=====================================================*/

Array.prototype.mySort = function(sName){
	sName = sName.CompareString();
	var out = new Array();
	var second = new Array();
	var matchFound = false;
	
	for(var i=0; i < this.length;i++){
		if(!matchFound){
			if(this[i].uCaseName.substring(0, sName.length) == sName){
				matchFound == true;
				out[out.length] = this[i];	
			}else{
			second[second.length] = this[i];
			}
		}else{
			out[out.length] = this[i];
		}
	}
	return out.concat(second);
}

Array.prototype.GetComboList = function(sVal, oComboBox, mustShow)
{

	var out = new Array();
	var nout = new Array();
	var arrS = this;
	var bNoMath = true;
	var bNoFirstLastMatch = true;
	CompareName = oComboBox.tDiv.value;

    for(var i=0; i<arrS.length; i++)
    {
        if(arrS[i].isTotalMatch(sVal))
        {
            out[out.length] = arrS[i];
            bNoMath = false;
            bNoFirstLastMatch = false;
        }
    }
    if(bNoFirstLastMatch)
    {
        for(var i=0; i<arrS.length; i++)
        {
            if(arrS[i].isPartialMatch(sVal))
            {
                out[out.length] = arrS[i];
                bNoMath = false;
            }
        }
    }
    if(bNoMath && bNoFirstLastMatch)
    {
        sVal = sVal.SoundString(false);
	    for(var i =0; i < arrS.length; i++)
	    {
		    if(arrS[i].SoundsLike(sVal)) 
		        out[out.length] = arrS[i];
	    }
    }
	
	//oComboBox.LastList = out;
	
	out = out.mySort(CompareName);
	
	oComboBox.LastList = out;
	for(var i =0; i < out.length; i++){
		nout[nout.length] = out[i].GetComboLink(nout.length);	
	}

	oComboBox.LastCompareName = CompareName;
	return nout;
}


String.prototype.CompareString = function(){
	return this.toUpperCase();
}

String.prototype.SoundString = function(bStripSpace){

    var strSound = '';

    strSound = this.toString().trim();

    if(bStripSpace)
        strSound = strSound.replace(/[^A-Z]/gi, ''); // rpl non-chars w emptyspace
    else
        strSound = strSound.replace(/[^A-Z\s]/gi, ''); // rpl non-chars w space but keep spaces

    /* handling english phonetics improvements*/
    strSound = strSound.replace(/DG/gi, 'G');     // Change DG to G
    strSound = strSound.replace(/GH/gi, 'H');     // Change GH to H
    strSound = strSound.replace(/GN/gi, 'N');     // Change GN to N
    strSound = strSound.replace(/KN/gi, 'N');     // Change KN to N
    strSound = strSound.replace(/PH/gi, 'F');     // Change PH to F
    strSound = strSound.replace(/MP([STZ])/gi, 'M$1'); // MP if fllwd by ST|Z
    strSound = strSound.replace(/^PS/gi, 'S');   // Chng leadng PS to S
    strSound = strSound.replace(/^PF/gi, 'F');   // Chng leadng PF to F
    strSound = strSound.replace(/MB/gi, 'M');    // Chng MB to M
    strSound = strSound.replace(/TCH/gi, 'CH');  // Chng TCH to CH

    /*double letters = 1 letter*/
    strSound = strSound.replace(/([A-Z])\1/gi, '$1'); 
    /* Begin Classic SoundEx */
    strSound = strSound.replace(/[AEIOUYHW]/gi, '');
    strSound = strSound.replace(/[BPFV]/gi, '1');
    strSound = strSound.replace(/[CSGJKQXZ]/gi, '2');
    strSound = strSound.replace(/[DT]/gi, '3');
    strSound = strSound.replace(/[L]/gi, '4');
    strSound = strSound.replace(/[MN]/gi, '5');
    strSound = strSound.replace(/[R]/gi, '6');


    return strSound;
}

String.prototype.removeDuplicates = function(){

	var lastLetter = '';
	
	for(var i = 0; i < this.length;i++){
		if(this.charAt(i) == lastLetter){
			return (this.substring(0, i) + this.substring(i + 1)).removeDuplicates();
		}
		lastLetter = this.charAt(i);
	}

	return this;
}

String.prototype.fromPx = function(){

	return parseInt(this.replace(/px/gi, ''), 10);	
	
}

/*=====================================================
	Support Functions, necessary for execution
=====================================================*/

var CompareName;
function SortByName(val1, val2)
{	
	if(val1.Name.substring(0, CompareName.length).toUpperCase() == CompareName.toUpperCase()) 
		return -1; 
	else if((val2.Name.substring(0, CompareName.length).toUpperCase() == CompareName.toUpperCase()))
		return 1;
	else {	
		if(val2.Name > val1.Name)
			return -1;
		else
			return 1;
	}
}

function SortByNameOnly(val1, val2)
{
	if(val2.Name > val1.Name)
		return -1;
	else
		return 1;
}

function getAbsoluteLeft(o) {
	// Get an object left position from the upper left viewport corner
	// Tested with relative and nested objects
	oLeft = o.offsetLeft;           // Get left position from the parent object
	while(o.offsetParent!=null) {   // Parse the parent hierarchy up to the document element
		oParent = o.offsetParent ;   // Get parent object reference
		oLeft += oParent.offsetLeft; // Add parent left position
		o = oParent;
	}
	// Return left postion
	return oLeft;
}

function getAbsoluteTop(o) {
	// Get an object top position from the upper left viewport corner
	// Tested with relative and nested objects
	oTop = o.offsetTop;            // Get top position from the parent object
	while(o.offsetParent!=null) { // Parse the parent hierarchy up to the document element
		oParent = o.offsetParent;  // Get parent object reference
		oTop += oParent.offsetTop; // Add parent top position
		o = oParent;
	}
	// Return top position
	return oTop;
}

function addEvent(elm, evType, fn)
{
	if(elm.addEventListener) {
		elm.addEventListener(evType, fn, false);
	}else if(elm.attachEvent) {
		var r = elm.attachEvent('on' + evType, fn);
		return r;
	} else {
		elm['on' + evType] = fn;
	}
}
