    /**
     *Object to hold all of the info we need to persist
     */
    var state = {
        _sList : null,
        _oSender : null,
        _iMax : null,
        _iMode : null,
        _sAlign : null,
        _sFilter : null,
        _bCancelling : null,
        _bWasSelect : false,
        _sFilterMode : null,
        _oEvent : new Object()
        };
        
    /**
     *Browser independent method of creating the XmlHttp Object
     *@return Object XmlHttp
     */
    function ajaxCreateRequest(){ 
        try{ 
            request = new ActiveXObject("Msxml2.XMLHTTP"); 
        }catch(e){ 
            try{ 
                request = new ActiveXObject("Microsoft.XMLHTTP"); 
            }catch(e2){ 
                request = false; 
            } 
        }          

        if(!request && typeof XMLHttpRequest != 'undefined'){ 
            request = new XMLHttpRequest(); 
        } 

        if(!request){ 
            alert("Error initializing XMLHttpRequest!"); 
        } 
        return request; 
    }
    
    /**
     *Initialise lists so that the '1st time lag' is minimal when selecting values
     *@param CSV in the format 'ListID#RequestMode,'
     */
    function initLists(sLists){
        var aLists = sLists.split(",");
        for(var ii = 0; ii < aLists.length; ii++){
            var aReq = aLists[ii].split("#");
            var request = ajaxCreateRequest();
            request.open("POST", "../AjaxHandler.do", true);
            request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            request.send("List=" + aReq[0] + "&Mode=" + aReq[1] + "&Filter=*&FilterMode=startswith");
        }
    }

    /**
     *Request a VTList via ajax
     *@param List to request
     *@param Sender component
     *@param Max 
     *@param Align mode for list
     */
    function ajaxRequestList(evt, sList, oSender, iMax, iMode, sAlign, sFilterMode){
        state._sList = sList;
        state._oSender = oSender;
        state._iMax = iMax;
        state._iMode = iMode;
        state._sAlign = sAlign;
        state._sFilter = obi(oSender.id).value;
        state._bCancelling = false;
        state._oEvent = evt;
        state._sFilterMode = (sFilterMode != null && typeof(sFilterMode) != 'undefined') ? sFilterMode : 'startswith';
        
        if(!checkKey()){
            return false;
        }
        
        state._oSender.setAttribute("autocomplete", "off");
        
        request = ajaxCreateRequest();
        request.onreadystatechange = ajaxProcessList;
        request.open("POST", "../AjaxHandler.do", true);
        request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        
        if(state._sFilter != ""){
            request.send("List=" + state._sList + "&Mode=" + state._iMode + "&Filter=" + state._sFilter + "&FilterMode=" + state._sFilterMode);   
        }
    }
 
    /**
     *Process a returned list into a select div popup
     */
    function ajaxProcessList(){
        if (request.readyState == 4){
            if (request.status == 200) {
                //alert(request.responseText);
                eval(request.responseText)
                obi('acMainContainer').innerHTML = "";
                
                sOut = "<div id='acOpts'>"
                var ii = 0;
                for(var val in oVTList){
                    if(ii >= state._iMax){
                        break;
                    }
                    
                    //Escape the value
                    if(val.indexOf("'") > -1){
                        var escVal = val.replace(/'/,"\\'");
                    }else{
                        var escVal = val;
                    }
                    
                    sOut += "<a class=\"acListItem\" href=\"Javascript:void(0)\" onfocus=\"Javascript:this.className='acListFocus';cancelHide();\" onblur=\"Javascript:this.className='acListBlur';hideListContainer();\" onkeydown=\"Javascript:return acListCycle(event, this);\" onclick=\"Javascript:state._bWasSelect = true;ajaxAssignListValue('" + escVal + "');\" onmousedown=\"Javascript:state._bWasSelect = true;ajaxAssignListValue('" + escVal + "');\">" + val + " </a>";
                    ii++;
                }
                
                if(ii == 0){
                    sOut += "<a class=\"notFoundItem\" href=\"Javascript:void(0)\" onfocus=\"Javascript:cancelHide();\" onblur=\"Javascript:hideListContainer();\" onkeydown=\"Javascript:return acListCycle(event, this);\" onclick=\"Javascript:state._bWasSelect = true;ajaxAssignListValue('');\">not found</a>";
                }
                
                sOut += '</div>';
                
                obi('acMainContainer').innerHTML = sOut;
                               
                drawListContainer();
            }
        }
    }
       
    /**
     *Set the sender components value with the selected value
     *@param the value to assign to the sender component
     */
    function ajaxAssignListValue(sVal){
        state._oSender.value = sVal;
        hideListContainer();
    }

    /**
     *Draw the popup div to hold the list
     */
    function drawListContainer(){
        var oBody = obi('body1');
        var oCont = obi('acOuterContainer');
        var oIframe = obi('acIframe');
        var oMain = obi('acMainContainer');
        
        if(oCont.parentNode == oBody){
            var oTemp = oBody.removeChild(oCont);
            state._oSender.parentNode.insertBefore(oTemp, state._oSender.nextSibling);
            state._oSender.parentNode.style.zIndex = "49999";
        }
        
        switch(state._sAlign){        
            case "base":
                //Under the sender component
                oCont.style.left = getElementLeft(state._oSender.id, false) + "px";
                oCont.style.top = getElementTop(state._oSender.id, false) + 22 + "px";
            break;
            
            default:
                //Right of the sender component
                oCont.style.top = getElementTop(state._oSender.id) + "px";
                oCont.style.left = (getElementLeft(state._oSender.id) + parseInt(state._oSender.style.width) + 10) + "px";
            break
        }
        
        oCont.style.visibility = "visible";
        
        //Size the iframe to match the content
        oIframe.style.height = oMain.offsetHeight + "px";
        oIframe.style.width = oMain.offsetWidth + "px";
        oIframe.height = oMain.offsetHeight + "px";
        oIframe.width = oMain.offsetWidth + "px";
        oIframe.style.top = "0px";
        oIframe.style.left = "0px";
        oIframe.style.display = "inline";
    }
        
    /**
     *Wrapper for hiding the list div so we can use setTimeout()
     */
    function hideListContainer(){
        state._bCancelling = false;
        var oCont = obi('acOuterContainer');
        if(oCont.style.visibility != "hidden"){
            setTimeout(_hideListContainer, 50);
        }
    }
    
    /**
     *Function for hiding the list div
     */
    function _hideListContainer(){
        if(!state._bCancelling){
            var oCont = obi('acOuterContainer');
            oCont.style.visibility = "hidden";
            var oBody = obi('body1');
            var oInlineParent = oCont.parentNode;
            var oTemp = oInlineParent.removeChild(oCont);
            oBody.appendChild(oTemp);
            oInlineParent.style.zIndex = "1";
            if(state._bWasSelect){
                state._oSender.select();
                state._oSender.focus();
            }
        }
    }
    
    /**
     *Adds a cancel state to state object so that the div is not hidden prematurely
     */
    function cancelHide(){
        state._bCancelling = true;
    }
    
    /**
     *Gets the left edge of a component in px
     */
    function getElementLeft(Elem, getAbs) {
        var elem = obi(Elem);
        xPos = elem.offsetLeft;
        tempEl = elem.offsetParent;
        if(getAbs){
            while (tempEl != null) {
                xPos += tempEl.offsetLeft;
                tempEl = tempEl.offsetParent;
            }
        }
        return xPos;
    }

    /**
     *Gets the top edge of a component in px
     */
    function getElementTop(Elem, getAbs) {	
        var elem = obi(Elem);

        yPos = elem.offsetTop;
        tempEl = elem.offsetParent;
        if(getAbs){
            while (tempEl != null) {
                yPos += tempEl.offsetTop;
                tempEl = tempEl.offsetParent;
            }
        }
        return yPos;
    }
    
    function checkKey(){
        var evt = state._oEvent;
        var key = (evt.which)?evt.which:evt.keyCode;

        var aValid = new Array( 32, 109, 190, 191, 192, 220 );
        
        if(document.all){ 
            aValid.push(189); //hyphen
            aValid.push(111); //F.slash numpad
            aValid.push(46); // . numpad
        }else{ 
            aValid.push(111); //F.slash numpad
            aValid.push(110); // . numpad            
        }
        
        var bValid = contains(key, aValid);
        
        if( (key < 65 || key > 90) && (key != 8) && (key != 46) && (!bValid) ){ 
            return false;
        }
        
        return true;
    }
    
    /**
     *Check a value is contained within a specified array
     */
    function contains(sVal, aArray){
        for(var ii = 0; ii < aArray.length; ii++){
            if(sVal == aArray[ii]){
                return true;
            }
        }
        return false;
    }
    
    /**
     *Check for up/down arrow presses in the autocomplete list
     *Refocus appropriately
     */
    function acListCycle(evt, obj){
        state._bWasSelect = false;
        var oCont = obi('acOuterContainer');
        var oTgt = evt.target || evt.srcElement; 
        if(oCont.style.visibility != "hidden"){
            var key = (evt.which)?evt.which:evt.keyCode;

            if( key == 38 ){ //Up
                if(obj.tagName.toLowerCase() == 'a'){
                    if(obj.previousSibling != null){
                        obj.previousSibling.focus();
                    }else{
                        state._oSender.focus();
                    }
                }
                return false;
            }

            if( key == 40 ){ //Down
                if(obj.tagName.toLowerCase() == 'a'){
                    if(obj.nextSibling != null){
                        obj.nextSibling.focus();
                    }
                }else{
                    var oFirst = obi('acMainContainer').getElementsByTagName('a')[0];
                    if(oFirst != null){
                        oFirst.focus();
                    }
                }
                return false;
            }
            
            if( (key == 8) && (oTgt != state._oSender) ){
                //prevent backspace taking us back a page
                return false;
            }
        }
        
    }
